diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 2fb33437e..000000000 --- a/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -.build -.git \ No newline at end of file diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 08891d83f..000000000 --- a/.editorconfig +++ /dev/null @@ -1,8 +0,0 @@ -root = true - -[*] -indent_style = space -indent_size = 4 -end_of_line = lf -insert_final_newline = true -trim_trailing_whitespace = true \ No newline at end of file diff --git a/.github/release.yml b/.github/release.yml deleted file mode 100644 index e29eb8464..000000000 --- a/.github/release.yml +++ /dev/null @@ -1,14 +0,0 @@ -changelog: - categories: - - title: SemVer Major - labels: - - ⚠️ semver/major - - title: SemVer Minor - labels: - - 🆕 semver/minor - - title: SemVer Patch - labels: - - 🔨 semver/patch - - title: Other Changes - labels: - - semver/none diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 3bf5a95ec..000000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Main - -on: - push: - branches: [main] - schedule: - - cron: "0 8,20 * * *" - -jobs: - unit-tests: - name: Unit tests - uses: apple/swift-nio/.github/workflows/unit_tests.yml@main - with: - linux_5_10_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -warnings-as-errors" - linux_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -warnings-as-errors" - linux_6_1_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -warnings-as-errors" - linux_nightly_next_arguments_override: "--explicit-target-dependency-import-check error" - linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error" - - static-sdk: - name: Static SDK - # Workaround https://github.com/nektos/act/issues/1875 - uses: apple/swift-nio/.github/workflows/static_sdk.yml@main diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml deleted file mode 100644 index 8036d7ad7..000000000 --- a/.github/workflows/pull_request.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: PR - -on: - pull_request: - types: [opened, reopened, synchronize] - -jobs: - soundness: - name: Soundness - uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main - with: - license_header_check_project_name: "AsyncHTTPClient" - unit-tests: - name: Unit tests - uses: apple/swift-nio/.github/workflows/unit_tests.yml@main - with: - linux_5_10_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -warnings-as-errors" - linux_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -warnings-as-errors" - linux_6_1_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -warnings-as-errors" - linux_nightly_next_arguments_override: "--explicit-target-dependency-import-check error" - linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error" - - cxx-interop: - name: Cxx interop - uses: apple/swift-nio/.github/workflows/cxx_interop.yml@main - with: - linux_5_9_enabled: false - - static-sdk: - name: Static SDK - # Workaround https://github.com/nektos/act/issues/1875 - uses: apple/swift-nio/.github/workflows/static_sdk.yml@main diff --git a/.github/workflows/pull_request_label.yml b/.github/workflows/pull_request_label.yml deleted file mode 100644 index 8fd47c13f..000000000 --- a/.github/workflows/pull_request_label.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: PR label - -on: - pull_request: - types: [labeled, unlabeled, opened, reopened, synchronize] - -jobs: - semver-label-check: - name: Semantic version label check - runs-on: ubuntu-latest - timeout-minutes: 1 - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - persist-credentials: false - - name: Check for Semantic Version label - uses: apple/swift-nio/.github/actions/pull_request_semver_label_checker@main diff --git a/.gitignore b/.gitignore deleted file mode 100644 index c71941a4c..000000000 --- a/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -.build -Package.resolved -*.xcodeproj -DerivedData -.DS_Store -.swiftpm/ -.SourceKitten diff --git a/.licenseignore b/.licenseignore deleted file mode 100644 index edceaab62..000000000 --- a/.licenseignore +++ /dev/null @@ -1,37 +0,0 @@ -.gitignore -**/.gitignore -.licenseignore -.gitattributes -.git-blame-ignore-revs -.mailfilter -.mailmap -.spi.yml -.swift-format -.editorconfig -.github/* -*.md -*.txt -*.yml -*.yaml -*.json -Package.swift -**/Package.swift -Package@-*.swift -**/Package@-*.swift -Package.resolved -**/Package.resolved -Makefile -*.modulemap -**/*.modulemap -**/*.docc/* -*.xcprivacy -**/*.xcprivacy -*.symlink -**/*.symlink -Dockerfile -**/Dockerfile -.dockerignore -Snippets/* -dev/git.commit.template -.unacceptablelanguageignore -Tests/AsyncHTTPClientTests/Resources/*.pem diff --git a/.mailmap b/.mailmap deleted file mode 100644 index b1b8e46a0..000000000 --- a/.mailmap +++ /dev/null @@ -1 +0,0 @@ -Artem Redkin diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 000000000..e69de29bb diff --git a/.spi.yml b/.spi.yml deleted file mode 100644 index 795484b35..000000000 --- a/.spi.yml +++ /dev/null @@ -1,4 +0,0 @@ -version: 1 -builder: - configs: - - documentation_targets: [AsyncHTTPClient] diff --git a/.swift-format b/.swift-format deleted file mode 100644 index 7e8ae7391..000000000 --- a/.swift-format +++ /dev/null @@ -1,68 +0,0 @@ -{ - "version" : 1, - "indentation" : { - "spaces" : 4 - }, - "tabWidth" : 4, - "fileScopedDeclarationPrivacy" : { - "accessLevel" : "private" - }, - "spacesAroundRangeFormationOperators" : false, - "indentConditionalCompilationBlocks" : false, - "indentSwitchCaseLabels" : false, - "lineBreakAroundMultilineExpressionChainComponents" : false, - "lineBreakBeforeControlFlowKeywords" : false, - "lineBreakBeforeEachArgument" : true, - "lineBreakBeforeEachGenericRequirement" : true, - "lineLength" : 120, - "maximumBlankLines" : 1, - "respectsExistingLineBreaks" : true, - "prioritizeKeepingFunctionOutputTogether" : true, - "noAssignmentInExpressions" : { - "allowedFunctions" : [ - "XCTAssertNoThrow", - "XCTAssertThrowsError" - ] - }, - "rules" : { - "AllPublicDeclarationsHaveDocumentation" : false, - "AlwaysUseLiteralForEmptyCollectionInit" : false, - "AlwaysUseLowerCamelCase" : false, - "AmbiguousTrailingClosureOverload" : true, - "BeginDocumentationCommentWithOneLineSummary" : false, - "DoNotUseSemicolons" : true, - "DontRepeatTypeInStaticProperties" : true, - "FileScopedDeclarationPrivacy" : true, - "FullyIndirectEnum" : true, - "GroupNumericLiterals" : true, - "IdentifiersMustBeASCII" : true, - "NeverForceUnwrap" : false, - "NeverUseForceTry" : false, - "NeverUseImplicitlyUnwrappedOptionals" : false, - "NoAccessLevelOnExtensionDeclaration" : true, - "NoAssignmentInExpressions" : true, - "NoBlockComments" : true, - "NoCasesWithOnlyFallthrough" : true, - "NoEmptyTrailingClosureParentheses" : true, - "NoLabelsInCasePatterns" : true, - "NoLeadingUnderscores" : false, - "NoParensAroundConditions" : true, - "NoVoidReturnOnFunctionSignature" : true, - "OmitExplicitReturns" : true, - "OneCasePerLine" : true, - "OneVariableDeclarationPerLine" : true, - "OnlyOneTrailingClosureArgument" : true, - "OrderedImports" : true, - "ReplaceForEachWithForLoop" : true, - "ReturnVoidInsteadOfEmptyTuple" : true, - "UseEarlyExits" : false, - "UseExplicitNilCheckInConditions" : false, - "UseLetInEveryBoundCaseVariable" : false, - "UseShorthandTypeNames" : true, - "UseSingleLinePropertyGetter" : false, - "UseSynthesizedInitializer" : false, - "UseTripleSlashForDocumentationComments" : true, - "UseWhereClausesInForLoops" : false, - "ValidateDocumentationComments" : false - } -} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 76501d7d6..000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,5 +0,0 @@ -# Code of Conduct - -The code of conduct for this project can be found at https://swift.org/code-of-conduct. - - diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index dddcb3ba4..000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,75 +0,0 @@ -## Legal - -By submitting a pull request, you represent that you have the right to license -your contribution to Apple and the community, and agree by submitting the patch -that your contributions are licensed under the Apache 2.0 license (see -`LICENSE.txt`). - - -## How to submit a bug report - -Please ensure to specify the following: - -* AsyncHTTPClient commit hash -* Contextual information (e.g. what you were trying to achieve with AsyncHTTPClient) -* Simplest possible steps to reproduce - * More complex the steps are, lower the priority will be. - * A pull request with failing test case is preferred, but it's just fine to paste the test case into the issue description. -* Anything that might be relevant in your opinion, such as: - * Swift version or the output of `swift --version` - * OS version and the output of `uname -a` - * Network configuration - - -### Example - -``` -AsyncHTTPClient commit hash: 22ec043dc9d24bb011b47ece4f9ee97ee5be2757 - -Context: -While load testing my program written with AsyncHTTPClient, I noticed -that one file descriptor is leaked per request. - -Steps to reproduce: -1. ... -2. ... -3. ... -4. ... - -$ swift --version -Swift version 4.0.2 (swift-4.0.2-RELEASE) -Target: x86_64-unknown-linux-gnu - -Operating system: Ubuntu Linux 16.04 64-bit - -$ uname -a -Linux beefy.machine 4.4.0-101-generic #124-Ubuntu SMP Fri Nov 10 18:29:59 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux - -My system has IPv6 disabled. -``` - -## Writing a Patch - -A good AsyncHTTPClient patch is: - -1. Concise, and contains as few changes as needed to achieve the end result. -2. Tested, ensuring that any tests provided failed before the patch and pass after it. -3. Documented, adding API documentation as needed to cover new functions and properties. -4. Accompanied by a great commit message, using our commit message template. - -*Note* as of version 1.10.0 AsyncHTTPClient requires Swift 5.4. Earlier versions support as far back as Swift 5.0. - -### Commit Message Template - -We require that your commit messages match our template. The easiest way to do that is to get git to help you by explicitly using the template. To do that, `cd` to the root of our repository and run: - - git config commit.template dev/git.commit.template - - -### Run CI checks locally - -You can run the Github Actions workflows locally using [act](https://github.com/nektos/act). For detailed steps on how to do this please see [https://github.com/swiftlang/github-workflows?tab=readme-ov-file#running-workflows-locally](https://github.com/swiftlang/github-workflows?tab=readme-ov-file#running-workflows-locally). - -## How to contribute your work - -Please open a pull request at https://github.com/swift-server/async-http-client. Make sure the CI passes, and then wait for code review. diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt deleted file mode 100644 index f0e7f8401..000000000 --- a/CONTRIBUTORS.txt +++ /dev/null @@ -1,30 +0,0 @@ -For the purpose of tracking copyright, this is the list of individuals and -organizations who have contributed source code to the AsyncHTTPClient. - -For employees of an organization/company where the copyright of work done -by employees of that company is held by the company itself, only the company -needs to be listed here. - -## COPYRIGHT HOLDERS - -- Apple Inc. (all contributors with '@apple.com') - -### Contributors - -- Andrew Lees <32634907+Andrew-Lees11@users.noreply.github.com> -- Artem Redkin -- George Barnett -- Ian Partridge -- Joe Smith -- Johannes Weiss -- Ludovic Dewailly -- Tanner -- Tobias -- Trevör -- tomer doron -- tomer doron -- vkill - -**Updating this list** - -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` diff --git a/Examples/GetHTML/GetHTML.swift b/Examples/GetHTML/GetHTML.swift deleted file mode 100644 index ca3bacbea..000000000 --- a/Examples/GetHTML/GetHTML.swift +++ /dev/null @@ -1,34 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2022 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import AsyncHTTPClient -import NIOCore - -@main -struct GetHTML { - static func main() async throws { - let httpClient = HTTPClient(eventLoopGroupProvider: .singleton) - do { - let request = HTTPClientRequest(url: "https://apple.com/") - let response = try await httpClient.execute(request, timeout: .seconds(30)) - print("HTTP head", response) - let body = try await response.body.collect(upTo: 1024 * 1024) // 1 MB - print(String(buffer: body)) - } catch { - print("request failed:", error) - } - // it is important to shutdown the httpClient after all requests are done, even if one failed - try await httpClient.shutdown() - } -} diff --git a/Examples/GetJSON/GetJSON.swift b/Examples/GetJSON/GetJSON.swift deleted file mode 100644 index 1af7a5144..000000000 --- a/Examples/GetJSON/GetJSON.swift +++ /dev/null @@ -1,52 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2022 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import AsyncHTTPClient -import Foundation -import NIOCore -import NIOFoundationCompat - -struct Comic: Codable { - var num: Int - var title: String - var day: String - var month: String - var year: String - var img: String - var alt: String - var news: String - var link: String - var transcript: String -} - -@main -struct GetJSON { - static func main() async throws { - let httpClient = HTTPClient(eventLoopGroupProvider: .singleton) - do { - let request = HTTPClientRequest(url: "https://xkcd.com/info.0.json") - let response = try await httpClient.execute(request, timeout: .seconds(30)) - print("HTTP head", response) - let body = try await response.body.collect(upTo: 1024 * 1024) // 1 MB - // we use an overload defined in `NIOFoundationCompat` for `decode(_:from:)` to - // efficiently decode from a `ByteBuffer` - let comic = try JSONDecoder().decode(Comic.self, from: body) - dump(comic) - } catch { - print("request failed:", error) - } - // it is important to shutdown the httpClient after all requests are done, even if one failed - try await httpClient.shutdown() - } -} diff --git a/Examples/Package.swift b/Examples/Package.swift deleted file mode 100644 index 9986b17b5..000000000 --- a/Examples/Package.swift +++ /dev/null @@ -1,67 +0,0 @@ -// swift-tools-version:5.5 -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2018-2022 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import PackageDescription - -let package = Package( - name: "async-http-client-examples", - platforms: [ - .macOS(.v10_15), - .iOS(.v13), - .tvOS(.v13), - .watchOS(.v6), - ], - products: [ - .executable(name: "GetHTML", targets: ["GetHTML"]), - .executable(name: "GetJSON", targets: ["GetJSON"]), - .executable(name: "StreamingByteCounter", targets: ["StreamingByteCounter"]), - ], - dependencies: [ - .package(url: "https://github.com/apple/swift-nio.git", from: "2.38.0"), - - // in real-world projects this would be - // .package(url: "https://github.com/swift-server/async-http-client.git", from: "1.9.0") - .package(name: "async-http-client", path: "../"), - ], - targets: [ - // MARK: - Examples - - .executableTarget( - name: "GetHTML", - dependencies: [ - .product(name: "AsyncHTTPClient", package: "async-http-client"), - .product(name: "NIOCore", package: "swift-nio"), - ], - path: "GetHTML" - ), - .executableTarget( - name: "GetJSON", - dependencies: [ - .product(name: "AsyncHTTPClient", package: "async-http-client"), - .product(name: "NIOCore", package: "swift-nio"), - .product(name: "NIOFoundationCompat", package: "swift-nio"), - ], - path: "GetJSON" - ), - .executableTarget( - name: "StreamingByteCounter", - dependencies: [ - .product(name: "AsyncHTTPClient", package: "async-http-client"), - .product(name: "NIOCore", package: "swift-nio"), - ], - path: "StreamingByteCounter" - ), - ] -) diff --git a/Examples/README.md b/Examples/README.md deleted file mode 100644 index 849999f99..000000000 --- a/Examples/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# Examples -This folder includes a couple of Examples for `AsyncHTTPClient`. -You can run them by opening the `Package.swift` in this folder through Xcode. -In Xcode you can then select the scheme for the example you want run e.g. `GetHTML`. - -You can also run the examples from the command line by executing the follow command in this folder: -``` -swift run GetHTML -``` -To run other examples you can just replace `GetHTML` with the name of the example you want to run. - -## [GetHTML](./GetHTML/GetHTML.swift) - -This examples sends a HTTP GET request to `https://apple.com/` and first `await`s and `print`s the HTTP Response Head. -Afterwards it buffers the full response body in memory and prints the response as a `String`. - -## [GetJSON](./GetJSON/GetJSON.swift) - -This examples sends a HTTP GET request to `https://xkcd.com/info.0.json` and first `await`s and `print`s the HTTP Response Head. -Afterwards it buffers the full response body in memory, decodes the buffer using a `JSONDecoder` and `dump`s the decoded response. - -## [StreamingByteCounter](./StreamingByteCounter/StreamingByteCounter.swift) - -This examples sends a HTTP GET request to `https://apple.com/` and first `await`s and `print`s the HTTP Response Head. -Afterwards it asynchronously iterates over all body fragments, counts the received bytes and prints a progress indicator (if the server send a content-length header). -At the end the total received bytes are printed. -Note that we drop all received fragment and therefore do **not** buffer the whole response body in-memory. diff --git a/Examples/StreamingByteCounter/StreamingByteCounter.swift b/Examples/StreamingByteCounter/StreamingByteCounter.swift deleted file mode 100644 index ecfb48776..000000000 --- a/Examples/StreamingByteCounter/StreamingByteCounter.swift +++ /dev/null @@ -1,50 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2022 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import AsyncHTTPClient -import NIOCore - -@main -struct StreamingByteCounter { - static func main() async throws { - let httpClient = HTTPClient(eventLoopGroupProvider: .singleton) - do { - let request = HTTPClientRequest(url: "https://apple.com/") - let response = try await httpClient.execute(request, timeout: .seconds(30)) - print("HTTP head", response) - - // if defined, the content-length headers announces the size of the body - let expectedBytes = response.headers.first(name: "content-length").flatMap(Int.init) - - var receivedBytes = 0 - // asynchronously iterates over all body fragments - // this loop will automatically propagate backpressure correctly - for try await buffer in response.body { - // For this example, we are just interested in the size of the fragment - receivedBytes += buffer.readableBytes - - if let expectedBytes = expectedBytes { - // if the body size is known, we calculate a progress indicator - let progress = Double(receivedBytes) / Double(expectedBytes) - print("progress: \(Int(progress * 100))%") - } - } - print("did receive \(receivedBytes) bytes") - } catch { - print("request failed:", error) - } - // it is important to shutdown the httpClient after all requests are done, even if one failed - try await httpClient.shutdown() - } -} diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index d64569567..000000000 --- a/LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/NOTICE.txt b/NOTICE.txt deleted file mode 100644 index 86a969171..000000000 --- a/NOTICE.txt +++ /dev/null @@ -1,62 +0,0 @@ - - The AsyncHTTPClient Project - =========================== - -Please visit the AsyncHTTPClient web site for more information: - - * https://github.com/swift-server/async-http-client - -Copyright 2017-2021 The AsyncHTTPClient Project - -The AsyncHTTPClient Project licenses this file to you under the Apache License, -version 2.0 (the "License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at: - - https://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -License for the specific language governing permissions and limitations -under the License. - -Also, please refer to each LICENSE.txt file, which is located in -the 'license' directory of the distribution file, for the license terms of the -components that this product depends on. - ---- - -This product contains derivations of various scripts from SwiftNIO. - - * LICENSE (Apache License 2.0): - * https://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * https://github.com/apple/swift-nio - ---- - -This product contains a derivation of "XCTest+AsyncAwait.swift" from gRPC Swift. - - * LICENSE (Apache License 2.0): - * https://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * https://github.com/grpc/grpc-swift - ---- - -This product contains a derivation of the Tony Stone's 'process_test_files.rb'. - - * LICENSE (Apache License 2.0): - * https://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * https://github.com/tonystone/build-tools/commit/6c417b7569df24597a48a9aa7b505b636e8f73a1 - * https://github.com/tonystone/build-tools/blob/cf3440f43bde2053430285b4ed0709c865892eb5/source/xctest_tool.rb - ---- - -This product contains a derivation of Fabian Fett's 'Base64.swift'. - - * LICENSE (Apache License 2.0): - * https://github.com/swift-extras/swift-extras-base64/blob/b8af49699d59ad065b801715a5009619100245ca/LICENSE - * HOMEPAGE: - * https://github.com/fabianfett/swift-base64-kit diff --git a/Package.swift b/Package.swift deleted file mode 100644 index 3294781a9..000000000 --- a/Package.swift +++ /dev/null @@ -1,121 +0,0 @@ -// swift-tools-version:5.10 -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2018-2019 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import PackageDescription - -let strictConcurrencyDevelopment = false - -let strictConcurrencySettings: [SwiftSetting] = { - var initialSettings: [SwiftSetting] = [] - initialSettings.append(contentsOf: [ - .enableUpcomingFeature("StrictConcurrency"), - .enableUpcomingFeature("InferSendableFromCaptures"), - ]) - - if strictConcurrencyDevelopment { - // -warnings-as-errors here is a workaround so that IDE-based development can - // get tripped up on -require-explicit-sendable. - initialSettings.append(.unsafeFlags(["-Xfrontend", "-require-explicit-sendable", "-warnings-as-errors"])) - } - - return initialSettings -}() - -let package = Package( - name: "async-http-client", - products: [ - .library(name: "AsyncHTTPClient", targets: ["AsyncHTTPClient"]) - ], - dependencies: [ - .package(url: "https://github.com/apple/swift-nio.git", from: "2.81.0"), - .package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.30.0"), - .package(url: "https://github.com/apple/swift-nio-http2.git", from: "1.36.0"), - .package(url: "https://github.com/apple/swift-nio-extras.git", from: "1.26.0"), - .package(url: "https://github.com/apple/swift-nio-transport-services.git", from: "1.24.0"), - .package(url: "https://github.com/apple/swift-log.git", from: "1.6.0"), - .package(url: "https://github.com/apple/swift-atomics.git", from: "1.0.2"), - .package(url: "https://github.com/apple/swift-algorithms.git", from: "1.0.0"), - ], - targets: [ - .target( - name: "CAsyncHTTPClient", - cSettings: [ - .define("_GNU_SOURCE") - ] - ), - .target( - name: "AsyncHTTPClient", - dependencies: [ - .target(name: "CAsyncHTTPClient"), - .product(name: "NIO", package: "swift-nio"), - .product(name: "NIOTLS", package: "swift-nio"), - .product(name: "NIOCore", package: "swift-nio"), - .product(name: "NIOPosix", package: "swift-nio"), - .product(name: "NIOHTTP1", package: "swift-nio"), - .product(name: "NIOConcurrencyHelpers", package: "swift-nio"), - .product(name: "NIOHTTP2", package: "swift-nio-http2"), - .product(name: "NIOSSL", package: "swift-nio-ssl"), - .product(name: "NIOHTTPCompression", package: "swift-nio-extras"), - .product(name: "NIOSOCKS", package: "swift-nio-extras"), - .product(name: "NIOTransportServices", package: "swift-nio-transport-services"), - .product(name: "Logging", package: "swift-log"), - .product(name: "Atomics", package: "swift-atomics"), - .product(name: "Algorithms", package: "swift-algorithms"), - ], - swiftSettings: strictConcurrencySettings - ), - .testTarget( - name: "AsyncHTTPClientTests", - dependencies: [ - .target(name: "AsyncHTTPClient"), - .product(name: "NIOTLS", package: "swift-nio"), - .product(name: "NIOCore", package: "swift-nio"), - .product(name: "NIOConcurrencyHelpers", package: "swift-nio"), - .product(name: "NIOEmbedded", package: "swift-nio"), - .product(name: "NIOFoundationCompat", package: "swift-nio"), - .product(name: "NIOTestUtils", package: "swift-nio"), - .product(name: "NIOSSL", package: "swift-nio-ssl"), - .product(name: "NIOHTTP2", package: "swift-nio-http2"), - .product(name: "NIOSOCKS", package: "swift-nio-extras"), - .product(name: "Logging", package: "swift-log"), - .product(name: "Atomics", package: "swift-atomics"), - .product(name: "Algorithms", package: "swift-algorithms"), - ], - resources: [ - .copy("Resources/self_signed_cert.pem"), - .copy("Resources/self_signed_key.pem"), - .copy("Resources/example.com.cert.pem"), - .copy("Resources/example.com.private-key.pem"), - ], - swiftSettings: strictConcurrencySettings - ), - ] -) - -// --- STANDARD CROSS-REPO SETTINGS DO NOT EDIT --- // -for target in package.targets { - switch target.type { - case .regular, .test, .executable: - var settings = target.swiftSettings ?? [] - // https://github.com/swiftlang/swift-evolution/blob/main/proposals/0444-member-import-visibility.md - settings.append(.enableUpcomingFeature("MemberImportVisibility")) - target.swiftSettings = settings - case .macro, .plugin, .system, .binary: - () // not applicable - @unknown default: - () // we don't know what to do here, do nothing - } -} -// --- END: STANDARD CROSS-REPO SETTINGS DO NOT EDIT --- // diff --git a/README.md b/README.md deleted file mode 100644 index a4f49c8c8..000000000 --- a/README.md +++ /dev/null @@ -1,321 +0,0 @@ -# AsyncHTTPClient -This package provides an HTTP Client library built on top of SwiftNIO. - -This library provides the following: -- First class support for Swift Concurrency -- Asynchronous and non-blocking request methods -- Simple follow-redirects (cookie headers are dropped) -- Streaming body download -- TLS support -- Automatic HTTP/2 over HTTPS -- Cookie parsing (but not storage) - -## Getting Started - -#### Adding the dependency -Add the following entry in your Package.swift to start using HTTPClient: - -```swift -.package(url: "https://github.com/swift-server/async-http-client.git", from: "1.9.0") -``` -and `AsyncHTTPClient` dependency to your target: -```swift -.target(name: "MyApp", dependencies: [.product(name: "AsyncHTTPClient", package: "async-http-client")]), -``` - -#### Request-Response API - -The code snippet below illustrates how to make a simple GET request to a remote server. - -```swift -import AsyncHTTPClient - -/// MARK: - Using Swift Concurrency -let request = HTTPClientRequest(url: "https://apple.com/") -let response = try await HTTPClient.shared.execute(request, timeout: .seconds(30)) -print("HTTP head", response) -if response.status == .ok { - let body = try await response.body.collect(upTo: 1024 * 1024) // 1 MB - // handle body -} else { - // handle remote error -} - - -/// MARK: - Using SwiftNIO EventLoopFuture -HTTPClient.shared.get(url: "https://apple.com/").whenComplete { result in - switch result { - case .failure(let error): - // process error - case .success(let response): - if response.status == .ok { - // handle response - } else { - // handle remote error - } - } -} -``` - -If you create your own `HTTPClient` instances, you should shut them down using `httpClient.shutdown()` when you're done using them. Failing to do so will leak resources. - Please note that you must not call `httpClient.shutdown` before all requests of the HTTP client have finished, or else the in-flight requests will likely fail because their network connections are interrupted. - -### async/await examples - -Examples for the async/await API can be found in the [`Examples` folder](./Examples) in this Repository. - -## Usage guide - -The default HTTP Method is `GET`. In case you need to have more control over the method, or you want to add headers or body, use the `HTTPClientRequest` struct: - -#### Using Swift Concurrency - -```swift -import AsyncHTTPClient - -do { - var request = HTTPClientRequest(url: "https://apple.com/") - request.method = .POST - request.headers.add(name: "User-Agent", value: "Swift HTTPClient") - request.body = .bytes(ByteBuffer(string: "some data")) - - let response = try await HTTPClient.shared.execute(request, timeout: .seconds(30)) - if response.status == .ok { - // handle response - } else { - // handle remote error - } -} catch { - // handle error -} -``` - -#### Using SwiftNIO EventLoopFuture - -```swift -import AsyncHTTPClient - -var request = try HTTPClient.Request(url: "https://apple.com/", method: .POST) -request.headers.add(name: "User-Agent", value: "Swift HTTPClient") -request.body = .string("some-body") - -HTTPClient.shared.execute(request: request).whenComplete { result in - switch result { - case .failure(let error): - // process error - case .success(let response): - if response.status == .ok { - // handle response - } else { - // handle remote error - } - } -} -``` - -### Redirects following - -The globally shared instance `HTTPClient.shared` follows redirects by default. If you create your own `HTTPClient`, you can enable the follow-redirects behavior using the client configuration: - -```swift -let httpClient = HTTPClient(eventLoopGroupProvider: .singleton, - configuration: HTTPClient.Configuration(followRedirects: true)) -``` - -### Timeouts -Timeouts (connect and read) can also be set using the client configuration: -```swift -let timeout = HTTPClient.Configuration.Timeout(connect: .seconds(1), read: .seconds(1)) -let httpClient = HTTPClient(eventLoopGroupProvider: .singleton, - configuration: HTTPClient.Configuration(timeout: timeout)) -``` -or on a per-request basis: -```swift -httpClient.execute(request: request, deadline: .now() + .milliseconds(1)) -``` - -### Streaming -When dealing with larger amount of data, it's critical to stream the response body instead of aggregating in-memory. -The following example demonstrates how to count the number of bytes in a streaming response body: - -#### Using Swift Concurrency -```swift -do { - let request = HTTPClientRequest(url: "https://apple.com/") - let response = try await HTTPClient.shared.execute(request, timeout: .seconds(30)) - print("HTTP head", response) - - // if defined, the content-length headers announces the size of the body - let expectedBytes = response.headers.first(name: "content-length").flatMap(Int.init) - - var receivedBytes = 0 - // asynchronously iterates over all body fragments - // this loop will automatically propagate backpressure correctly - for try await buffer in response.body { - // for this example, we are just interested in the size of the fragment - receivedBytes += buffer.readableBytes - - if let expectedBytes = expectedBytes { - // if the body size is known, we calculate a progress indicator - let progress = Double(receivedBytes) / Double(expectedBytes) - print("progress: \(Int(progress * 100))%") - } - } - print("did receive \(receivedBytes) bytes") -} catch { - print("request failed:", error) -} -``` - -#### Using HTTPClientResponseDelegate and SwiftNIO EventLoopFuture - -```swift -import NIOCore -import NIOHTTP1 - -class CountingDelegate: HTTPClientResponseDelegate { - typealias Response = Int - - var count = 0 - - func didSendRequestHead(task: HTTPClient.Task, _ head: HTTPRequestHead) { - // this is executed right after request head was sent, called once - } - - func didSendRequestPart(task: HTTPClient.Task, _ part: IOData) { - // this is executed when request body part is sent, could be called zero or more times - } - - func didSendRequest(task: HTTPClient.Task) { - // this is executed when request is fully sent, called once - } - - func didReceiveHead( - task: HTTPClient.Task, - _ head: HTTPResponseHead - ) -> EventLoopFuture { - // this is executed when we receive HTTP response head part of the request - // (it contains response code and headers), called once in case backpressure - // is needed, all reads will be paused until returned future is resolved - return task.eventLoop.makeSucceededFuture(()) - } - - func didReceiveBodyPart( - task: HTTPClient.Task, - _ buffer: ByteBuffer - ) -> EventLoopFuture { - // this is executed when we receive parts of the response body, could be called zero or more times - count += buffer.readableBytes - // in case backpressure is needed, all reads will be paused until returned future is resolved - return task.eventLoop.makeSucceededFuture(()) - } - - func didFinishRequest(task: HTTPClient.Task) throws -> Int { - // this is called when the request is fully read, called once - // this is where you return a result or throw any errors you require to propagate to the client - return count - } - - func didReceiveError(task: HTTPClient.Task, _ error: Error) { - // this is called when we receive any network-related error, called once - } -} - -let request = try HTTPClient.Request(url: "https://apple.com/") -let delegate = CountingDelegate() - -HTTPClient.shared.execute(request: request, delegate: delegate).futureResult.whenSuccess { count in - print(count) -} -``` - -### File downloads - -Based on the `HTTPClientResponseDelegate` example above you can build more complex delegates, -the built-in `FileDownloadDelegate` is one of them. It allows streaming the downloaded data -asynchronously, while reporting the download progress at the same time, like in the following -example: - -```swift -let request = try HTTPClient.Request( - url: "https://swift.org/builds/development/ubuntu1804/latest-build.yml" -) - -let delegate = try FileDownloadDelegate(path: "/tmp/latest-build.yml", reportProgress: { - if let totalBytes = $0.totalBytes { - print("Total bytes count: \(totalBytes)") - } - print("Downloaded \($0.receivedBytes) bytes so far") -}) - -HTTPClient.shared.execute(request: request, delegate: delegate).futureResult - .whenSuccess { progress in - if let totalBytes = progress.totalBytes { - print("Final total bytes count: \(totalBytes)") - } - print("Downloaded finished with \(progress.receivedBytes) bytes downloaded") - } -``` - -### Unix Domain Socket Paths -Connecting to servers bound to socket paths is easy: -```swift -HTTPClient.shared.execute( - .GET, - socketPath: "/tmp/myServer.socket", - urlPath: "/path/to/resource" -).whenComplete (...) -``` - -Connecting over TLS to a unix domain socket path is possible as well: -```swift -HTTPClient.shared.execute( - .POST, - secureSocketPath: "/tmp/myServer.socket", - urlPath: "/path/to/resource", - body: .string("hello") -).whenComplete (...) -``` - -Direct URLs can easily be constructed to be executed in other scenarios: -```swift -let socketPathBasedURL = URL( - httpURLWithSocketPath: "/tmp/myServer.socket", - uri: "/path/to/resource" -) -let secureSocketPathBasedURL = URL( - httpsURLWithSocketPath: "/tmp/myServer.socket", - uri: "/path/to/resource" -) -``` - -### Disabling HTTP/2 -The exclusive use of HTTP/1 is possible by setting `httpVersion` to `.http1Only` on `HTTPClient.Configuration`: -```swift -var configuration = HTTPClient.Configuration() -configuration.httpVersion = .http1Only -let client = HTTPClient( - eventLoopGroupProvider: .singleton, - configuration: configuration -) -``` - -## Security - -Please have a look at [SECURITY.md](SECURITY.md) for AsyncHTTPClient's security process. - -## Supported Versions - -The most recent versions of AsyncHTTPClient support Swift 5.10 and newer. The minimum Swift version supported by AsyncHTTPClient releases are detailed below: - -AsyncHTTPClient | Minimum Swift Version ---------------------|---------------------- -`1.0.0 ..< 1.5.0` | 5.0 -`1.5.0 ..< 1.10.0` | 5.2 -`1.10.0 ..< 1.13.0` | 5.4 -`1.13.0 ..< 1.18.0` | 5.5.2 -`1.18.0 ..< 1.20.0` | 5.6 -`1.20.0 ..< 1.21.0` | 5.7 -`1.21.0 ..< 1.26.0` | 5.8 -`1.26.0 ..< 1.27.0` | 5.9 -`1.27.0 ...` | 5.10 diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 3492df406..000000000 --- a/SECURITY.md +++ /dev/null @@ -1,44 +0,0 @@ -# Security - -This document specifies the security process for the AsyncHTTPClient project. - -## Disclosures - -### Private Disclosure Process - -The AsyncHTTPClient maintainers ask that known and suspected vulnerabilities be -privately and responsibly disclosed by emailing -[sswg-security-reports@forums.swift.org](mailto:sswg-security-reports@forums.swift.org) -with the all the required detail. -**Do not file a public issue.** - -#### When to report a vulnerability - -* You think you have discovered a potential security vulnerability in - AsyncHTTPClient. -* You are unsure how a vulnerability affects AsyncHTTPClient. - -#### What happens next? - -* A member of the team will acknowledge receipt of the report within 3 - working days (United States). This may include a request for additional - information about reproducing the vulnerability. -* We will privately inform the Swift Server Work Group ([SSWG][sswg]) of the - vulnerability within 10 days of the report as per their [security - guidelines][sswg-security]. -* Once we have identified a fix we may ask you to validate it. We aim to do this - within 30 days. In some cases this may not be possible, for example when the - vulnerability exists at the protocol level and the industry must coordinate on - the disclosure process. -* If a CVE number is required, one will be requested from [MITRE][mitre] - providing you with full credit for the discovery. -* We will decide on a planned release date and let you know when it is. -* Prior to release, we will inform major dependents that a security-related - patch is impending. -* Once the fix has been released we will publish a security advisory on GitHub - and in the Server → Security Updates category on the [Swift forums][swift-forums-sec]. - -[sswg]: https://github.com/swift-server/sswg -[sswg-security]: https://github.com/swift-server/sswg/blob/main/security/README.md -[swift-forums-sec]: https://forums.swift.org/c/server/security-updates/ -[mitre]: https://cveform.mitre.org/ diff --git a/Sources/AsyncHTTPClient/AsyncAwait/AnyAsyncSequence.swift b/Sources/AsyncHTTPClient/AsyncAwait/AnyAsyncSequence.swift deleted file mode 100644 index fbcc82ec1..000000000 --- a/Sources/AsyncHTTPClient/AsyncAwait/AnyAsyncSequence.swift +++ /dev/null @@ -1,51 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2022 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@usableFromInline -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -struct AnyAsyncSequence: Sendable, AsyncSequence { - @usableFromInline typealias AsyncIteratorNextCallback = () async throws -> Element? - - @usableFromInline struct AsyncIterator: AsyncIteratorProtocol { - @usableFromInline let nextCallback: AsyncIteratorNextCallback - - @inlinable init(nextCallback: @escaping AsyncIteratorNextCallback) { - self.nextCallback = nextCallback - } - - @inlinable mutating func next() async throws -> Element? { - try await self.nextCallback() - } - } - - @usableFromInline var makeAsyncIteratorCallback: @Sendable () -> AsyncIteratorNextCallback - - @inlinable init( - _ asyncSequence: SequenceOfBytes - ) where SequenceOfBytes: AsyncSequence & Sendable, SequenceOfBytes.Element == Element { - self.makeAsyncIteratorCallback = { - var iterator = asyncSequence.makeAsyncIterator() - return { - try await iterator.next() - } - } - } - - @inlinable func makeAsyncIterator() -> AsyncIterator { - .init(nextCallback: self.makeAsyncIteratorCallback()) - } -} - -@available(*, unavailable) -extension AnyAsyncSequence.AsyncIterator: Sendable {} diff --git a/Sources/AsyncHTTPClient/AsyncAwait/AnyAsyncSequenceProucerDelete.swift b/Sources/AsyncHTTPClient/AsyncAwait/AnyAsyncSequenceProucerDelete.swift deleted file mode 100644 index 1e35df7f2..000000000 --- a/Sources/AsyncHTTPClient/AsyncAwait/AnyAsyncSequenceProucerDelete.swift +++ /dev/null @@ -1,37 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2023 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -@usableFromInline -struct AnyAsyncSequenceProducerDelegate: NIOAsyncSequenceProducerDelegate { - @usableFromInline - var delegate: NIOAsyncSequenceProducerDelegate - - @inlinable - init(_ delegate: Delegate) { - self.delegate = delegate - } - - @inlinable - func produceMore() { - self.delegate.produceMore() - } - - @inlinable - func didTerminate() { - self.delegate.didTerminate() - } -} diff --git a/Sources/AsyncHTTPClient/AsyncAwait/AsyncLazySequence.swift b/Sources/AsyncHTTPClient/AsyncAwait/AsyncLazySequence.swift deleted file mode 100644 index fe37dd5e7..000000000 --- a/Sources/AsyncHTTPClient/AsyncAwait/AsyncLazySequence.swift +++ /dev/null @@ -1,52 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2022 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -@usableFromInline -struct AsyncLazySequence: AsyncSequence { - @usableFromInline typealias Element = Base.Element - @usableFromInline struct AsyncIterator: AsyncIteratorProtocol { - @usableFromInline var iterator: Base.Iterator - @inlinable init(iterator: Base.Iterator) { - self.iterator = iterator - } - - @inlinable mutating func next() async throws -> Base.Element? { - self.iterator.next() - } - } - - @usableFromInline var base: Base - - @inlinable init(base: Base) { - self.base = base - } - - @inlinable func makeAsyncIterator() -> AsyncIterator { - .init(iterator: self.base.makeIterator()) - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension AsyncLazySequence: Sendable where Base: Sendable {} -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension AsyncLazySequence.AsyncIterator: Sendable where Base.Iterator: Sendable {} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension Sequence { - /// Turns `self` into an `AsyncSequence` by vending each element of `self` asynchronously. - @inlinable var async: AsyncLazySequence { - .init(base: self) - } -} diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClient+execute.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClient+execute.swift deleted file mode 100644 index 5fc1be9f5..000000000 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClient+execute.swift +++ /dev/null @@ -1,253 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOCore -import NIOHTTP1 - -import struct Foundation.URL - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension HTTPClient { - /// Execute arbitrary HTTP requests. - /// - /// - Parameters: - /// - request: HTTP request to execute. - /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. - /// - /// - warning: This method may violates Structured Concurrency because it returns a `HTTPClientResponse` that needs to be - /// streamed by the user. This means the request, the connection and other resources are still alive when the request returns. - /// - /// - Returns: The response to the request. Note that the `body` of the response may not yet have been fully received. - public func execute( - _ request: HTTPClientRequest, - deadline: NIODeadline, - logger: Logger? = nil - ) async throws -> HTTPClientResponse { - try await self.executeAndFollowRedirectsIfNeeded( - request, - deadline: deadline, - logger: logger ?? Self.loggingDisabled, - redirectState: RedirectState(self.configuration.redirectConfiguration.mode, initialURL: request.url) - ) - } -} - -// MARK: Connivence methods - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension HTTPClient { - /// Execute arbitrary HTTP requests. - /// - /// - Parameters: - /// - request: HTTP request to execute. - /// - timeout: time the the request has to complete. - /// - logger: The logger to use for this request. - /// - /// - warning: This method may violates Structured Concurrency because it returns a `HTTPClientResponse` that needs to be - /// streamed by the user. This means the request, the connection and other resources are still alive when the request returns. - /// - /// - Returns: The response to the request. Note that the `body` of the response may not yet have been fully received. - public func execute( - _ request: HTTPClientRequest, - timeout: TimeAmount, - logger: Logger? = nil - ) async throws -> HTTPClientResponse { - try await self.execute( - request, - deadline: .now() + timeout, - logger: logger - ) - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension HTTPClient { - /// - warning: This method may violates Structured Concurrency because it returns a `HTTPClientResponse` that needs to be - /// streamed by the user. This means the request, the connection and other resources are still alive when the request returns. - private func executeAndFollowRedirectsIfNeeded( - _ request: HTTPClientRequest, - deadline: NIODeadline, - logger: Logger, - redirectState: RedirectState? - ) async throws -> HTTPClientResponse { - var currentRequest = request - var currentRedirectState = redirectState - var history: [HTTPClientRequestResponse] = [] - - // this loop is there to follow potential redirects - while true { - let preparedRequest = try HTTPClientRequest.Prepared(currentRequest, dnsOverride: configuration.dnsOverride) - let response = try await { - var response = try await self.executeCancellable(preparedRequest, deadline: deadline, logger: logger) - - history.append( - .init( - request: currentRequest, - responseHead: .init( - version: response.version, - status: response.status, - headers: response.headers - ) - ) - ) - - response.history = history - - return response - }() - - guard var redirectState = currentRedirectState else { - // a `nil` redirectState means we should not follow redirects - return response - } - - guard - let redirectURL = response.headers.extractRedirectTarget( - status: response.status, - originalURL: preparedRequest.url, - originalScheme: preparedRequest.poolKey.scheme - ) - else { - // response does not want a redirect - return response - } - - // validate that we do not exceed any limits or are running circles - try redirectState.redirect(to: redirectURL.absoluteString) - currentRedirectState = redirectState - - let newRequest = currentRequest.followingRedirect( - from: preparedRequest.url, - to: redirectURL, - status: response.status - ) - - guard newRequest.body.canBeConsumedMultipleTimes else { - // we already send the request body and it cannot be send again - return response - } - - currentRequest = newRequest - } - } - - /// - warning: This method may violates Structured Concurrency because it returns a `HTTPClientResponse` that needs to be - /// streamed by the user. This means the request, the connection and other resources are still alive when the request returns. - private func executeCancellable( - _ request: HTTPClientRequest.Prepared, - deadline: NIODeadline, - logger: Logger - ) async throws -> HTTPClientResponse { - let cancelHandler = TransactionCancelHandler() - - return try await withTaskCancellationHandler( - operation: { () async throws -> HTTPClientResponse in - let eventLoop = self.eventLoopGroup.any() - let deadlineTask = eventLoop.scheduleTask(deadline: deadline) { - cancelHandler.cancel(reason: .deadlineExceeded) - } - defer { - deadlineTask.cancel() - } - return try await withCheckedThrowingContinuation { - (continuation: CheckedContinuation) -> Void in - let transaction = Transaction( - request: request, - requestOptions: .fromClientConfiguration(self.configuration), - logger: logger, - connectionDeadline: .now() + (self.configuration.timeout.connectionCreationTimeout), - preferredEventLoop: eventLoop, - responseContinuation: continuation - ) - - cancelHandler.registerTransaction(transaction) - - self.poolManager.executeRequest(transaction) - } - }, - onCancel: { - cancelHandler.cancel(reason: .taskCanceled) - } - ) - } -} - -/// There is currently no good way to asynchronously cancel an object that is initiated inside the `body` closure of `with*Continuation`. -/// As a workaround we use `TransactionCancelHandler` which will take care of the race between instantiation of `Transaction` -/// in the `body` closure and cancelation from the `onCancel` closure of `withTaskCancellationHandler`. -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -private actor TransactionCancelHandler { - enum CancelReason { - /// swift concurrency task was canceled - case taskCanceled - /// deadline timeout - case deadlineExceeded - } - - private enum State { - case initialised - case register(Transaction) - case cancelled(CancelReason) - } - - private var state: State = .initialised - - init() {} - - private func cancelTransaction(_ transaction: Transaction, for reason: CancelReason) { - switch reason { - case .taskCanceled: - transaction.cancel() - case .deadlineExceeded: - transaction.deadlineExceeded() - } - } - - private func _registerTransaction(_ transaction: Transaction) { - switch self.state { - case .initialised: - self.state = .register(transaction) - case .cancelled(let reason): - self.cancelTransaction(transaction, for: reason) - case .register: - preconditionFailure("transaction already set") - } - } - - nonisolated func registerTransaction(_ transaction: Transaction) { - Task { - await self._registerTransaction(transaction) - } - } - - private func _cancel(reason: CancelReason) { - switch self.state { - case .register(let transaction): - self.state = .cancelled(reason) - self.cancelTransaction(transaction, for: reason) - case .cancelled: - break - case .initialised: - self.state = .cancelled(reason) - } - } - - nonisolated func cancel(reason: CancelReason) { - Task { - await self._cancel(reason: reason) - } - } -} diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClient+shutdown.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClient+shutdown.swift deleted file mode 100644 index 43020c3e5..000000000 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClient+shutdown.swift +++ /dev/null @@ -1,32 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2022 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore - -extension HTTPClient { - /// Shuts down the client and `EventLoopGroup` if it was created by the client. - @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) - public func shutdown() async throws { - try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in - self.shutdown { error in - switch error { - case .none: - continuation.resume() - case .some(let error): - continuation.resume(throwing: error) - } - } - } - } -} diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest+Prepared.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest+Prepared.swift deleted file mode 100644 index c39452897..000000000 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest+Prepared.swift +++ /dev/null @@ -1,131 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOHTTP1 -import NIOSSL - -import struct Foundation.URL - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension HTTPClientRequest { - struct Prepared { - enum Body { - case asyncSequence( - length: RequestBodyLength, - makeAsyncIterator: @Sendable () -> ((ByteBufferAllocator) async throws -> ByteBuffer?) - ) - case sequence( - length: RequestBodyLength, - canBeConsumedMultipleTimes: Bool, - makeCompleteBody: (ByteBufferAllocator) -> ByteBuffer - ) - case byteBuffer(ByteBuffer) - } - - var url: URL - var poolKey: ConnectionPool.Key - var requestFramingMetadata: RequestFramingMetadata - var head: HTTPRequestHead - var body: Body? - var tlsConfiguration: TLSConfiguration? - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension HTTPClientRequest.Prepared { - init(_ request: HTTPClientRequest, dnsOverride: [String: String] = [:]) throws { - guard !request.url.isEmpty, let url = URL(string: request.url) else { - throw HTTPClientError.invalidURL - } - - let deconstructedURL = try DeconstructedURL(url: url) - - var headers = request.headers - headers.addHostIfNeeded(for: deconstructedURL) - let metadata = try headers.validateAndSetTransportFraming( - method: request.method, - bodyLength: .init(request.body) - ) - - self.init( - url: url, - poolKey: .init(url: deconstructedURL, tlsConfiguration: request.tlsConfiguration, dnsOverride: dnsOverride), - requestFramingMetadata: metadata, - head: .init( - version: .http1_1, - method: request.method, - uri: deconstructedURL.uri, - headers: headers - ), - body: request.body.map { .init($0) }, - tlsConfiguration: request.tlsConfiguration - ) - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension HTTPClientRequest.Prepared.Body { - init(_ body: HTTPClientRequest.Body) { - switch body.mode { - case .asyncSequence(let length, let makeAsyncIterator): - self = .asyncSequence(length: length, makeAsyncIterator: makeAsyncIterator) - case .sequence(let length, let canBeConsumedMultipleTimes, let makeCompleteBody): - self = .sequence( - length: length, - canBeConsumedMultipleTimes: canBeConsumedMultipleTimes, - makeCompleteBody: makeCompleteBody - ) - case .byteBuffer(let byteBuffer): - self = .byteBuffer(byteBuffer) - } - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension RequestBodyLength { - init(_ body: HTTPClientRequest.Body?) { - switch body?.mode { - case .none: - self = .known(0) - case .byteBuffer(let buffer): - self = .known(Int64(buffer.readableBytes)) - case .sequence(let length, _, _), .asyncSequence(let length, _): - self = length - } - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension HTTPClientRequest { - func followingRedirect( - from originalURL: URL, - to redirectURL: URL, - status: HTTPResponseStatus - ) -> HTTPClientRequest { - let (method, headers, body) = transformRequestForRedirect( - from: originalURL, - method: self.method, - headers: self.headers, - body: self.body, - to: redirectURL, - status: status - ) - var newRequest = HTTPClientRequest(url: redirectURL.absoluteString) - newRequest.method = method - newRequest.headers = headers - newRequest.body = body - return newRequest - } -} diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest+auth.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest+auth.swift deleted file mode 100644 index 106a8f76b..000000000 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest+auth.swift +++ /dev/null @@ -1,27 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2024 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension HTTPClientRequest { - /// Set basic auth for a request. - /// - /// - parameters: - /// - username: the username to authenticate with - /// - password: authentication password associated with the username - public mutating func setBasicAuth(username: String, password: String) { - self.headers.setBasicAuth(username: username, password: password) - } -} diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift deleted file mode 100644 index dca7de0ef..000000000 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest.swift +++ /dev/null @@ -1,429 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Algorithms -import NIOCore -import NIOHTTP1 -import NIOSSL - -@usableFromInline -let bagOfBytesToByteBufferConversionChunkSize = 1024 * 1024 * 4 - -#if arch(arm) || arch(i386) -// on 32-bit platforms we can't make use of a whole UInt32.max (as it doesn't fit in an Int) -@usableFromInline -let byteBufferMaxSize = Int.max -#else -// on 64-bit platforms we're good -@usableFromInline -let byteBufferMaxSize = Int(UInt32.max) -#endif - -/// A representation of an HTTP request for the Swift Concurrency HTTPClient API. -/// -/// This object is similar to ``HTTPClient/Request``, but used for the Swift Concurrency API. -/// -/// - note: For many ``HTTPClientRequest/body-swift.property`` configurations, this type is _not_ a value type -/// (https://github.com/swift-server/async-http-client/issues/708). -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -public struct HTTPClientRequest: Sendable { - /// The request URL, including scheme, hostname, and optionally port. - public var url: String - - /// The request method. - public var method: HTTPMethod - - /// The request headers. - public var headers: HTTPHeaders - - /// The request body, if any. - public var body: Body? - - /// Request-specific TLS configuration, defaults to no request-specific TLS configuration. - public var tlsConfiguration: TLSConfiguration? - - public init(url: String) { - self.url = url - self.method = .GET - self.headers = .init() - self.body = .none - self.tlsConfiguration = nil - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension HTTPClientRequest { - /// An HTTP request body. - /// - /// This object encapsulates the difference between streamed HTTP request bodies and those bodies that - /// are already entirely in memory. - public struct Body: Sendable { - @usableFromInline - internal enum Mode: Sendable { - /// - parameters: - /// - length: complete body length. - /// If `length` is `.known`, `nextBodyPart` is not allowed to produce more bytes than `length` defines. - /// - makeAsyncIterator: Creates a new async iterator under the hood and returns a function which will call `next()` on it. - /// The returned function then produce the next body buffer asynchronously. - /// We use a closure as an abstraction instead of an existential to enable specialization. - case asyncSequence( - length: RequestBodyLength, - makeAsyncIterator: @Sendable () -> ((ByteBufferAllocator) async throws -> ByteBuffer?) - ) - /// - parameters: - /// - length: complete body length. - /// If `length` is `.known`, `nextBodyPart` is not allowed to produce more bytes than `length` defines. - /// - canBeConsumedMultipleTimes: if `makeBody` can be called multiple times and returns the same result. - /// - makeCompleteBody: function to produce the complete body. - case sequence( - length: RequestBodyLength, - canBeConsumedMultipleTimes: Bool, - makeCompleteBody: @Sendable (ByteBufferAllocator) -> ByteBuffer - ) - case byteBuffer(ByteBuffer) - } - - @usableFromInline - internal var mode: Mode - - @inlinable - internal init(_ mode: Mode) { - self.mode = mode - } - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension HTTPClientRequest.Body { - /// Create an ``HTTPClientRequest/Body-swift.struct`` from a `ByteBuffer`. - /// - /// - parameter byteBuffer: The bytes of the body. - public static func bytes(_ byteBuffer: ByteBuffer) -> Self { - self.init(.byteBuffer(byteBuffer)) - } - - /// Create an ``HTTPClientRequest/Body-swift.struct`` from a `RandomAccessCollection` of bytes. - /// - /// This construction will flatten the `bytes` into a `ByteBuffer` in chunks of ~4MB. - /// As a result, the peak memory usage of this construction will be a small multiple of ~4MB. - /// The construction of the `ByteBuffer` will be delayed until it's needed. - /// - /// - parameter bytes: The bytes of the request body. - @inlinable - @preconcurrency - public static func bytes( - _ bytes: Bytes - ) -> Self where Bytes.Element == UInt8 { - self.bytes(bytes, length: .known(Int64(bytes.count))) - } - - /// Create an ``HTTPClientRequest/Body-swift.struct`` from a `Sequence` of bytes. - /// - /// This construction will flatten the bytes into a `ByteBuffer`. As a result, the peak memory - /// usage of this construction will be double the size of the original collection. The construction - /// of the `ByteBuffer` will be delayed until it's needed. - /// - /// Unlike ``bytes(_:)-1uns7``, this construction does not assume that the body can be replayed. As a result, - /// if a redirect is encountered that would need us to replay the request body, the redirect will instead - /// not be followed. Prefer ``bytes(_:)-1uns7`` wherever possible. - /// - /// Caution should be taken with this method to ensure that the `length` is correct. Incorrect lengths - /// will cause unnecessary runtime failures. Setting `length` to ``Length/unknown`` will trigger the upload - /// to use `chunked` `Transfer-Encoding`, while using ``Length/known(_:)-9q0ge`` will use `Content-Length`. - /// - /// - parameters: - /// - bytes: The bytes of the request body. - /// - length: The length of the request body. - @inlinable - @preconcurrency - public static func bytes( - _ bytes: Bytes, - length: Length - ) -> Self where Bytes.Element == UInt8 { - Self._bytes( - bytes, - length: length, - bagOfBytesToByteBufferConversionChunkSize: bagOfBytesToByteBufferConversionChunkSize, - byteBufferMaxSize: byteBufferMaxSize - ) - } - - /// internal method to test chunking - @inlinable - @preconcurrency - static func _bytes( - _ bytes: Bytes, - length: Length, - bagOfBytesToByteBufferConversionChunkSize: Int, - byteBufferMaxSize: Int - ) -> Self where Bytes.Element == UInt8 { - // fast path - let body: Self? = bytes.withContiguousStorageIfAvailable { bufferPointer -> Self in - // `some Sequence` is special as it can't be efficiently chunked lazily. - // Therefore we need to do the chunking eagerly if it implements the fast path withContiguousStorageIfAvailable - // If we do it eagerly, it doesn't make sense to do a bunch of small chunks, so we only chunk if it exceeds - // the maximum size of a ByteBuffer. - if bufferPointer.count <= byteBufferMaxSize { - let buffer = ByteBuffer(bytes: bufferPointer) - return Self( - .sequence( - length: length.storage, - canBeConsumedMultipleTimes: true, - makeCompleteBody: { _ in buffer } - ) - ) - } else { - // we need to copy `bufferPointer` eagerly as the pointer is only valid during the call to `withContiguousStorageIfAvailable` - let buffers: [ByteBuffer] = bufferPointer.chunks(ofCount: byteBufferMaxSize).map { - ByteBuffer(bytes: $0) - } - return Self( - .asyncSequence( - length: length.storage, - makeAsyncIterator: { - var iterator = buffers.makeIterator() - return { _ in - iterator.next() - } - } - ) - ) - } - } - if let body = body { - return body - } - - // slow path - return Self( - .asyncSequence( - length: length.storage - ) { - var iterator = bytes.makeIterator() - return { allocator in - var buffer = allocator.buffer(capacity: bagOfBytesToByteBufferConversionChunkSize) - while buffer.writableBytes > 0, let byte = iterator.next() { - buffer.writeInteger(byte) - } - if buffer.readableBytes > 0 { - return buffer - } - return nil - } - } - ) - } - - /// Create an ``HTTPClientRequest/Body-swift.struct`` from a `Collection` of bytes. - /// - /// This construction will flatten the `bytes` into a `ByteBuffer` in chunks of ~4MB. - /// As a result, the peak memory usage of this construction will be a small multiple of ~4MB. - /// The construction of the `ByteBuffer` will be delayed until it's needed. - /// - /// Caution should be taken with this method to ensure that the `length` is correct. Incorrect lengths - /// will cause unnecessary runtime failures. Setting `length` to ``Length/unknown`` will trigger the upload - /// to use `chunked` `Transfer-Encoding`, while using ``Length/known(_:)-9q0ge`` will use `Content-Length`. - /// - /// - parameters: - /// - bytes: The bytes of the request body. - /// - length: The length of the request body. - @inlinable - @preconcurrency - public static func bytes( - _ bytes: Bytes, - length: Length - ) -> Self where Bytes.Element == UInt8 { - if bytes.count <= bagOfBytesToByteBufferConversionChunkSize { - return self.init( - .sequence( - length: length.storage, - canBeConsumedMultipleTimes: true - ) { allocator in - allocator.buffer(bytes: bytes) - } - ) - } else { - return self.init( - .asyncSequence( - length: length.storage, - makeAsyncIterator: { - var iterator = bytes.chunks(ofCount: bagOfBytesToByteBufferConversionChunkSize).makeIterator() - return { allocator in - guard let chunk = iterator.next() else { - return nil - } - return allocator.buffer(bytes: chunk) - } - } - ) - ) - } - } - - /// Create an ``HTTPClientRequest/Body-swift.struct`` from an `AsyncSequence` of `ByteBuffer`s. - /// - /// This construction will stream the upload one `ByteBuffer` at a time. - /// - /// Caution should be taken with this method to ensure that the `length` is correct. Incorrect lengths - /// will cause unnecessary runtime failures. Setting `length` to ``Length/unknown`` will trigger the upload - /// to use `chunked` `Transfer-Encoding`, while using ``Length/known(_:)-9q0ge`` will use `Content-Length`. - /// - /// - parameters: - /// - sequenceOfBytes: The bytes of the request body. - /// - length: The length of the request body. - @inlinable - @preconcurrency - public static func stream( - _ sequenceOfBytes: SequenceOfBytes, - length: Length - ) -> Self where SequenceOfBytes.Element == ByteBuffer { - let body = self.init( - .asyncSequence(length: length.storage) { - var iterator = sequenceOfBytes.makeAsyncIterator() - return { _ -> ByteBuffer? in - try await iterator.next() - } - } - ) - return body - } - - /// Create an ``HTTPClientRequest/Body-swift.struct`` from an `AsyncSequence` of bytes. - /// - /// This construction will consume 4MB chunks from the `Bytes` and send them at once. This optimizes for - /// `AsyncSequence`s where larger chunks are buffered up and available without actually suspending, such - /// as those provided by `FileHandle`. - /// - /// Caution should be taken with this method to ensure that the `length` is correct. Incorrect lengths - /// will cause unnecessary runtime failures. Setting `length` to ``Length/unknown`` will trigger the upload - /// to use `chunked` `Transfer-Encoding`, while using ``Length/known(_:)-9q0ge`` will use `Content-Length`. - /// - /// - parameters: - /// - bytes: The bytes of the request body. - /// - length: The length of the request body. - @inlinable - @preconcurrency - public static func stream( - _ bytes: Bytes, - length: Length - ) -> Self where Bytes.Element == UInt8 { - let body = self.init( - .asyncSequence(length: length.storage) { - var iterator = bytes.makeAsyncIterator() - return { allocator -> ByteBuffer? in - var buffer = allocator.buffer(capacity: bagOfBytesToByteBufferConversionChunkSize) - while buffer.writableBytes > 0, let byte = try await iterator.next() { - buffer.writeInteger(byte) - } - if buffer.readableBytes > 0 { - return buffer - } - return nil - } - } - ) - return body - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension Optional where Wrapped == HTTPClientRequest.Body { - internal var canBeConsumedMultipleTimes: Bool { - switch self?.mode { - case .none: return true - case .byteBuffer: return true - case .sequence(_, let canBeConsumedMultipleTimes, _): return canBeConsumedMultipleTimes - case .asyncSequence: return false - } - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension HTTPClientRequest.Body { - /// The length of a HTTP request body. - public struct Length: Sendable { - /// The size of the request body is not known before starting the request - public static let unknown: Self = .init(storage: .unknown) - - /// The size of the request body is known and exactly `count` bytes - @available(*, deprecated, message: "Use `known(_ count: Int64)` with an explicit Int64 argument instead") - public static func known(_ count: Int) -> Self { - .init(storage: .known(Int64(count))) - } - - /// The size of the request body is known and exactly `count` bytes - public static func known(_ count: Int64) -> Self { - .init(storage: .known(count)) - } - - @usableFromInline - internal var storage: RequestBodyLength - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension HTTPClientRequest.Body: AsyncSequence { - public typealias Element = ByteBuffer - - @inlinable - public func makeAsyncIterator() -> AsyncIterator { - switch self.mode { - case .asyncSequence(_, let makeAsyncIterator): - return .init(storage: .makeNext(makeAsyncIterator())) - case .sequence(_, _, let makeCompleteBody): - return .init(storage: .byteBuffer(makeCompleteBody(AsyncIterator.allocator))) - case .byteBuffer(let byteBuffer): - return .init(storage: .byteBuffer(byteBuffer)) - } - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension HTTPClientRequest.Body { - public struct AsyncIterator: AsyncIteratorProtocol { - @usableFromInline - static let allocator = ByteBufferAllocator() - - @usableFromInline - enum Storage { - case byteBuffer(ByteBuffer?) - case makeNext((ByteBufferAllocator) async throws -> ByteBuffer?) - } - - @usableFromInline - var storage: Storage - - @inlinable - init(storage: Storage) { - self.storage = storage - } - - @inlinable - public mutating func next() async throws -> ByteBuffer? { - switch self.storage { - case .byteBuffer(let buffer): - self.storage = .byteBuffer(nil) - return buffer - case .makeNext(let makeNext): - return try await makeNext(Self.allocator) - } - } - } -} - -@available(*, unavailable) -extension HTTPClientRequest.Body.AsyncIterator: Sendable {} - -@available(*, unavailable) -extension HTTPClientRequest.Body.AsyncIterator.Storage: Sendable {} diff --git a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift b/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift deleted file mode 100644 index 36c1cb36f..000000000 --- a/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift +++ /dev/null @@ -1,267 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOHTTP1 - -import struct Foundation.URL - -/// A representation of an HTTP response for the Swift Concurrency HTTPClient API. -/// -/// This object is similar to ``HTTPClient/Response``, but used for the Swift Concurrency API. -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -public struct HTTPClientResponse: Sendable { - /// The HTTP version on which the response was received. - public var version: HTTPVersion - - /// The HTTP status for this response. - public var status: HTTPResponseStatus - - /// The HTTP headers of this response. - public var headers: HTTPHeaders - - /// The body of this HTTP response. - public var body: Body - - /// The history of all requests and responses in redirect order. - public var history: [HTTPClientRequestResponse] - - /// The target URL (after redirects) of the response. - public var url: URL? { - guard let lastRequestURL = self.history.last?.request.url else { - return nil - } - - return URL(string: lastRequestURL) - } - - @inlinable public init( - version: HTTPVersion = .http1_1, - status: HTTPResponseStatus = .ok, - headers: HTTPHeaders = [:], - body: Body = Body() - ) { - self.version = version - self.status = status - self.headers = headers - self.body = body - self.history = [] - } - - @inlinable public init( - version: HTTPVersion = .http1_1, - status: HTTPResponseStatus = .ok, - headers: HTTPHeaders = [:], - body: Body = Body(), - history: [HTTPClientRequestResponse] = [] - ) { - self.version = version - self.status = status - self.headers = headers - self.body = body - self.history = history - } - - init( - requestMethod: HTTPMethod, - version: HTTPVersion, - status: HTTPResponseStatus, - headers: HTTPHeaders, - body: TransactionBody, - history: [HTTPClientRequestResponse] - ) { - self.init( - version: version, - status: status, - headers: headers, - body: .init( - .transaction( - body, - expectedContentLength: HTTPClientResponse.expectedContentLength( - requestMethod: requestMethod, - headers: headers, - status: status - ) - ) - ), - history: history - ) - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -public struct HTTPClientRequestResponse: Sendable { - public var request: HTTPClientRequest - public var responseHead: HTTPResponseHead - - public init(request: HTTPClientRequest, responseHead: HTTPResponseHead) { - self.request = request - self.responseHead = responseHead - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension HTTPClientResponse { - /// A representation of the response body for an HTTP response. - /// - /// The body is streamed as an `AsyncSequence` of `ByteBuffer`, where each `ByteBuffer` contains - /// an arbitrarily large chunk of data. The boundaries between `ByteBuffer` objects in the sequence - /// are entirely synthetic and have no semantic meaning. - public struct Body: AsyncSequence, Sendable { - public typealias Element = ByteBuffer - public struct AsyncIterator: AsyncIteratorProtocol { - @usableFromInline var storage: Storage.AsyncIterator - - @inlinable init(storage: Storage.AsyncIterator) { - self.storage = storage - } - - @inlinable public mutating func next() async throws -> ByteBuffer? { - try await self.storage.next() - } - } - - @usableFromInline var storage: Storage - - @inlinable public func makeAsyncIterator() -> AsyncIterator { - .init(storage: self.storage.makeAsyncIterator()) - } - - @inlinable init(storage: Storage) { - self.storage = storage - } - - /// Accumulates `Body` of `ByteBuffer`s into a single `ByteBuffer`. - /// - Parameters: - /// - maxBytes: The maximum number of bytes this method is allowed to accumulate - /// - Throws: `NIOTooManyBytesError` if the the sequence contains more than `maxBytes`. - /// - Returns: the number of bytes collected over time - @inlinable public func collect(upTo maxBytes: Int) async throws -> ByteBuffer { - switch self.storage { - case .transaction(_, let expectedContentLength): - if let contentLength = expectedContentLength { - if contentLength > maxBytes { - throw NIOTooManyBytesError(maxBytes: maxBytes) - } - } - case .anyAsyncSequence: - break - } - - /// calling collect function within here in order to ensure the correct nested type - func collect(_ body: Body, maxBytes: Int) async throws -> ByteBuffer - where Body.Element == ByteBuffer { - try await body.collect(upTo: maxBytes) - } - return try await collect(self, maxBytes: maxBytes) - } - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension HTTPClientResponse { - static func expectedContentLength( - requestMethod: HTTPMethod, - headers: HTTPHeaders, - status: HTTPResponseStatus - ) -> Int? { - if status == .notModified { - return 0 - } else if requestMethod == .HEAD { - return 0 - } else { - let contentLength = headers["content-length"].first.flatMap { Int($0, radix: 10) } - return contentLength - } - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -@usableFromInline -typealias TransactionBody = NIOThrowingAsyncSequenceProducer< - ByteBuffer, - Error, - NIOAsyncSequenceProducerBackPressureStrategies.HighLowWatermark, - AnyAsyncSequenceProducerDelegate -> - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension HTTPClientResponse.Body { - @usableFromInline enum Storage: Sendable { - case transaction(TransactionBody, expectedContentLength: Int?) - case anyAsyncSequence(AnyAsyncSequence) - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension HTTPClientResponse.Body.Storage: AsyncSequence { - @usableFromInline typealias Element = ByteBuffer - - @inlinable func makeAsyncIterator() -> AsyncIterator { - switch self { - case .transaction(let transaction, _): - return .transaction(transaction.makeAsyncIterator()) - case .anyAsyncSequence(let anyAsyncSequence): - return .anyAsyncSequence(anyAsyncSequence.makeAsyncIterator()) - } - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension HTTPClientResponse.Body.Storage { - @usableFromInline enum AsyncIterator { - case transaction(TransactionBody.AsyncIterator) - case anyAsyncSequence(AnyAsyncSequence.AsyncIterator) - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension HTTPClientResponse.Body.Storage.AsyncIterator: AsyncIteratorProtocol { - @inlinable mutating func next() async throws -> ByteBuffer? { - switch self { - case .transaction(let iterator): - return try await iterator.next() - case .anyAsyncSequence(var iterator): - defer { self = .anyAsyncSequence(iterator) } - return try await iterator.next() - } - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension HTTPClientResponse.Body { - @inlinable init(_ storage: Storage) { - self.storage = storage - } - - public init() { - self = .stream(EmptyCollection().async) - } - - @inlinable public static func stream( - _ sequenceOfBytes: SequenceOfBytes - ) -> Self where SequenceOfBytes: AsyncSequence & Sendable, SequenceOfBytes.Element == ByteBuffer { - Self(storage: .anyAsyncSequence(AnyAsyncSequence(sequenceOfBytes.singleIteratorPrecondition))) - } - - public static func bytes(_ byteBuffer: ByteBuffer) -> Self { - .stream(CollectionOfOne(byteBuffer).async) - } -} - -@available(*, unavailable) -extension HTTPClientResponse.Body.AsyncIterator: Sendable {} - -@available(*, unavailable) -extension HTTPClientResponse.Body.Storage.AsyncIterator: Sendable {} diff --git a/Sources/AsyncHTTPClient/AsyncAwait/SingleIteratorPrecondition.swift b/Sources/AsyncHTTPClient/AsyncAwait/SingleIteratorPrecondition.swift deleted file mode 100644 index 04034db2d..000000000 --- a/Sources/AsyncHTTPClient/AsyncAwait/SingleIteratorPrecondition.swift +++ /dev/null @@ -1,45 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2022 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Atomics - -/// Makes sure that a consumer of this `AsyncSequence` only calls `makeAsyncIterator()` at most once. -/// If `makeAsyncIterator()` is called multiple times, the program crashes. -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -@usableFromInline struct SingleIteratorPrecondition: AsyncSequence { - @usableFromInline let base: Base - @usableFromInline let didCreateIterator: ManagedAtomic = .init(false) - @usableFromInline typealias Element = Base.Element - @inlinable init(base: Base) { - self.base = base - } - - @inlinable func makeAsyncIterator() -> Base.AsyncIterator { - precondition( - self.didCreateIterator.exchange(true, ordering: .relaxed) == false, - "makeAsyncIterator() is only allowed to be called at most once." - ) - return self.base.makeAsyncIterator() - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension SingleIteratorPrecondition: @unchecked Sendable where Base: Sendable {} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension AsyncSequence { - @inlinable var singleIteratorPrecondition: SingleIteratorPrecondition { - .init(base: self) - } -} diff --git a/Sources/AsyncHTTPClient/AsyncAwait/Transaction+StateMachine.swift b/Sources/AsyncHTTPClient/AsyncAwait/Transaction+StateMachine.swift deleted file mode 100644 index 457627a8a..000000000 --- a/Sources/AsyncHTTPClient/AsyncAwait/Transaction+StateMachine.swift +++ /dev/null @@ -1,557 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOCore -import NIOHTTP1 - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension Transaction { - @usableFromInline - struct StateMachine { - struct ExecutionContext { - let executor: HTTPRequestExecutor - let allocator: ByteBufferAllocator - let continuation: CheckedContinuation - } - - private enum State { - case initialized(CheckedContinuation) - case queued(CheckedContinuation, HTTPRequestScheduler) - case deadlineExceededWhileQueued(CheckedContinuation) - case executing(ExecutionContext, RequestStreamState, ResponseStreamState) - case finished(error: Error?) - } - - fileprivate enum RequestStreamState: Sendable { - case requestHeadSent - case producing - case paused(continuation: CheckedContinuation?) - case finished - } - - fileprivate enum ResponseStreamState: Sendable { - // Waiting for response head. Valid transitions to: streamingBody. - case waitingForResponseHead - // streaming response body. Valid transitions to: finished. - case streamingBody(TransactionBody.Source) - case finished - } - - private var state: State - - init(_ continuation: CheckedContinuation) { - self.state = .initialized(continuation) - } - - mutating func requestWasQueued(_ scheduler: HTTPRequestScheduler) { - guard case .initialized(let continuation) = self.state else { - // There might be a race between `requestWasQueued` and `willExecuteRequest`: - // - // If the request is created and passed to the HTTPClient on thread A, it will move into - // the connection pool lock in thread A. If no connection is available, thread A will - // add the request to the waiters and leave the connection pool lock. - // `requestWasQueued` will be called outside the connection pool lock on thread A. - // However if thread B has a connection that becomes available and thread B enters the - // connection pool lock directly after thread A, the request will be immediately - // scheduled for execution on thread B. After the thread B has left the lock it will - // call `willExecuteRequest` directly after. - // - // Having an order in the connection pool lock, does not guarantee an order in calling: - // `requestWasQueued` and `willExecuteRequest`. - // - // For this reason we must check the state here... If we are not `.initialized`, we are - // already executing. - return - } - - self.state = .queued(continuation, scheduler) - } - - enum FailAction { - case none - /// fail response before head received. scheduler and executor are exclusive here. - case failResponseHead( - CheckedContinuation, - Error, - HTTPRequestScheduler?, - HTTPRequestExecutor?, - bodyStreamContinuation: CheckedContinuation? - ) - /// fail response after response head received. fail the response stream (aka call to `next()`) - case failResponseStream( - TransactionBody.Source, - Error, - HTTPRequestExecutor, - bodyStreamContinuation: CheckedContinuation? - ) - - case failRequestStreamContinuation(CheckedContinuation, Error) - } - - mutating func fail(_ error: Error) -> FailAction { - switch self.state { - case .initialized(let continuation): - self.state = .finished(error: error) - return .failResponseHead(continuation, error, nil, nil, bodyStreamContinuation: nil) - - case .queued(let continuation, let scheduler): - self.state = .finished(error: error) - return .failResponseHead(continuation, error, scheduler, nil, bodyStreamContinuation: nil) - case .deadlineExceededWhileQueued(let continuation): - let realError: Error = { - if (error as? HTTPClientError) == .cancelled { - /// if we just get a `HTTPClientError.cancelled` we can use the original cancellation reason - /// to give a more descriptive error to the user. - return HTTPClientError.deadlineExceeded - } else { - /// otherwise we already had an intermediate connection error which we should present to the user instead - return error - } - }() - - self.state = .finished(error: realError) - return .failResponseHead(continuation, realError, nil, nil, bodyStreamContinuation: nil) - case .executing(let context, let requestStreamState, .waitingForResponseHead): - switch requestStreamState { - case .paused(continuation: .some(let continuation)): - self.state = .finished(error: error) - return .failResponseHead( - context.continuation, - error, - nil, - context.executor, - bodyStreamContinuation: continuation - ) - - case .requestHeadSent, .finished, .producing, .paused(continuation: .none): - self.state = .finished(error: error) - return .failResponseHead( - context.continuation, - error, - nil, - context.executor, - bodyStreamContinuation: nil - ) - } - - case .executing(let context, let requestStreamState, .streamingBody(let source)): - self.state = .finished(error: error) - switch requestStreamState { - case .paused(let bodyStreamContinuation): - return .failResponseStream( - source, - error, - context.executor, - bodyStreamContinuation: bodyStreamContinuation - ) - case .finished, .producing, .requestHeadSent: - return .failResponseStream(source, error, context.executor, bodyStreamContinuation: nil) - } - - case .finished(error: _), - .executing(_, _, .finished): - return .none - } - } - - // MARK: - Request - - - enum StartExecutionAction { - case cancel(HTTPRequestExecutor) - case cancelAndFail(HTTPRequestExecutor, CheckedContinuation, with: Error) - case none - } - - mutating func willExecuteRequest(_ executor: HTTPRequestExecutor) -> StartExecutionAction { - switch self.state { - case .initialized(let continuation), .queued(let continuation, _): - let context = ExecutionContext( - executor: executor, - allocator: .init(), - continuation: continuation - ) - self.state = .executing(context, .requestHeadSent, .waitingForResponseHead) - return .none - case .deadlineExceededWhileQueued(let continuation): - let error = HTTPClientError.deadlineExceeded - self.state = .finished(error: error) - return .cancelAndFail(executor, continuation, with: error) - - case .finished(error: .some): - return .cancel(executor) - - case .executing, - .finished(error: .none): - preconditionFailure("Invalid state: \(self.state)") - } - } - - enum ResumeProducingAction { - case startStream(ByteBufferAllocator) - case resumeStream(CheckedContinuation) - case none - } - - mutating func resumeRequestBodyStream() -> ResumeProducingAction { - switch self.state { - case .initialized, .queued, .deadlineExceededWhileQueued: - preconditionFailure( - "Received a resumeBodyRequest on a request, that isn't executing. Invalid state: \(self.state)" - ) - - case .executing(let context, .requestHeadSent, let responseState): - // the request can start to send its body. - self.state = .executing(context, .producing, responseState) - return .startStream(context.allocator) - - case .executing(_, .producing, _): - preconditionFailure( - "Received a resumeBodyRequest on a request, that is producing. Invalid state: \(self.state)" - ) - - case .executing(let context, .paused(.none), let responseState): - // request stream is currently paused, but there is no write waiting. We don't need - // to do anything. - self.state = .executing(context, .producing, responseState) - return .none - - case .executing(let context, .paused(.some(let continuation)), let responseState): - // the request body was paused. we can start the body streaming again. - self.state = .executing(context, .producing, responseState) - return .resumeStream(continuation) - - case .executing(_, .finished, _): - // the channels writability changed to writable after we have forwarded all the - // request bytes. Can be ignored. - return .none - - case .finished: - return .none - } - } - - mutating func pauseRequestBodyStream() { - switch self.state { - case .initialized, - .queued, - .deadlineExceededWhileQueued, - .executing(_, .requestHeadSent, _): - preconditionFailure("A request stream can only be resumed, if the request was started") - - case .executing(let context, .producing, let responseSteam): - self.state = .executing(context, .paused(continuation: nil), responseSteam) - - case .executing(_, .paused, _), - .executing(_, .finished, _), - .finished: - // the channels writability changed to paused after we have already forwarded all - // request bytes. Can be ignored. - break - } - } - - enum NextWriteAction { - case writeAndContinue(HTTPRequestExecutor) - case writeAndWait(HTTPRequestExecutor) - case fail - } - - func writeNextRequestPart() -> NextWriteAction { - switch self.state { - case .initialized, - .queued, - .deadlineExceededWhileQueued, - .executing(_, .requestHeadSent, _): - preconditionFailure( - "A request stream can only produce, if the request was started. Invalid state: \(self.state)" - ) - - case .executing(let context, .producing, _): - // We are currently producing the request body. The executors channel is writable. - // For this reason we can continue to produce data. - return .writeAndContinue(context.executor) - - case .executing(let context, .paused(continuation: .none), _): - // We are currently pausing the request body, since the executor's channel is not - // writable. We receive this call, since we were writable when we received the last - // data. At that point, we wanted to produce more. While waiting for more request - // bytes, the channel became not writable. - // - // Now is the point to pause producing. The user is required to call - // `writeNextRequestPart(continuation: )` next. - return .writeAndWait(context.executor) - - case .executing(_, .paused(continuation: .some), _): - preconditionFailure( - "A write continuation already exists, but we tried to set another one. Invalid state: \(self.state)" - ) - - case .finished, .executing(_, .finished, _): - return .fail - } - } - - mutating func waitForRequestBodyDemand(continuation: CheckedContinuation) { - switch self.state { - case .initialized, - .queued, - .deadlineExceededWhileQueued, - .executing(_, .requestHeadSent, _), - .executing(_, .finished, _): - preconditionFailure( - "A request stream can only produce, if the request was started. Invalid state: \(self.state)" - ) - - case .executing(_, .producing, _): - preconditionFailure() - - case .executing(_, .paused(continuation: .some), _): - preconditionFailure() - - case .executing(let context, .paused(continuation: .none), let responseState): - // We are currently pausing the request body, since the executor's channel is not - // writable. We receive this call, since we were writable when we received the last - // data. At that point, we wanted to produce more. While waiting for more request - // bytes, the channel became not writable. Now is the point to pause producing. - self.state = .executing(context, .paused(continuation: continuation), responseState) - - case .finished: - preconditionFailure() - } - } - - enum FinishAction { - // forward the notice that the request stream has finished. - case forwardStreamFinished(HTTPRequestExecutor) - case none - } - - mutating func finishRequestBodyStream() -> FinishAction { - switch self.state { - case .initialized, - .queued, - .deadlineExceededWhileQueued, - .executing(_, .finished, _): - preconditionFailure("Invalid state: \(self.state)") - - case .executing(_, .paused(continuation: .some), _): - preconditionFailure( - "Received a request body end, while having a registered back-pressure continuation. Invalid state: \(self.state)" - ) - - case .executing(let context, .producing, let responseState), - .executing(let context, .paused(continuation: .none), let responseState), - .executing(let context, .requestHeadSent, let responseState): - - switch responseState { - case .finished: - // if the response stream has already finished before the request, we must succeed - // the final continuation. - self.state = .finished(error: nil) - return .forwardStreamFinished(context.executor) - - case .waitingForResponseHead, .streamingBody: - self.state = .executing(context, .finished, responseState) - return .forwardStreamFinished(context.executor) - } - - case .finished: - return .none - } - } - - // MARK: - Response - - - enum ReceiveResponseHeadAction { - case succeedResponseHead(TransactionBody, CheckedContinuation) - case none - } - - mutating func receiveResponseHead( - _ head: HTTPResponseHead, - delegate: Delegate - ) -> ReceiveResponseHeadAction { - switch self.state { - case .initialized, - .queued, - .deadlineExceededWhileQueued, - .executing(_, _, .streamingBody), - .executing(_, _, .finished): - preconditionFailure("invalid state \(self.state)") - - case .executing(let context, let requestState, .waitingForResponseHead): - // The response head was received. Next we will wait for the consumer to create a - // response body stream. - let body = TransactionBody.makeSequence( - backPressureStrategy: .init(lowWatermark: 1, highWatermark: 1), - finishOnDeinit: true, - delegate: AnyAsyncSequenceProducerDelegate(delegate) - ) - - self.state = .executing(context, requestState, .streamingBody(body.source)) - return .succeedResponseHead(body.sequence, context.continuation) - - case .finished(error: .some): - // If the request failed before, we don't need to do anything in response to - // receiving the response head. - return .none - - case .finished(error: .none): - preconditionFailure("How can the request be finished without error, before receiving response head?") - } - } - - enum ProduceMoreAction { - case none - case requestMoreResponseBodyParts(HTTPRequestExecutor) - } - - mutating func produceMore() -> ProduceMoreAction { - switch self.state { - case .initialized, - .queued, - .deadlineExceededWhileQueued, - .executing(_, _, .waitingForResponseHead): - preconditionFailure("invalid state \(self.state)") - - case .executing(let context, _, .streamingBody): - return .requestMoreResponseBodyParts(context.executor) - case .finished, - .executing(_, _, .finished): - return .none - } - } - - enum ReceiveResponsePartAction { - case none - case yieldResponseBodyParts(TransactionBody.Source, CircularBuffer, HTTPRequestExecutor) - } - - mutating func receiveResponseBodyParts(_ buffer: CircularBuffer) -> ReceiveResponsePartAction { - switch self.state { - case .initialized, .queued, .deadlineExceededWhileQueued: - preconditionFailure( - "Received a response body part, but request hasn't started yet. Invalid state: \(self.state)" - ) - - case .executing(_, _, .waitingForResponseHead): - preconditionFailure("If we receive a response body, we must have received a head before") - - case .executing(let context, _, .streamingBody(let source)): - return .yieldResponseBodyParts(source, buffer, context.executor) - - case .finished: - // the request failed or was cancelled before, we can ignore further data - return .none - - case .executing(_, _, .finished): - preconditionFailure( - "Received response end. Must not receive further body parts after that. Invalid state: \(self.state)" - ) - } - } - - enum ReceiveResponseEndAction { - case finishResponseStream(TransactionBody.Source, finalBody: CircularBuffer?) - case none - } - - mutating func succeedRequest(_ newChunks: CircularBuffer?) -> ReceiveResponseEndAction { - switch self.state { - case .initialized, - .queued, - .deadlineExceededWhileQueued, - .executing(_, _, .waitingForResponseHead): - preconditionFailure( - "Received no response head, but received a response end. Invalid state: \(self.state)" - ) - - case .executing(let context, let requestState, .streamingBody(let source)): - self.state = .executing(context, requestState, .finished) - return .finishResponseStream(source, finalBody: newChunks) - case .finished: - // the request failed or was cancelled before, we can ignore all events - return .none - case .executing(_, _, .finished): - preconditionFailure( - "Already received an eof or error before. Must not receive further events. Invalid state: \(self.state)" - ) - } - } - - enum DeadlineExceededAction { - case none - case cancelSchedulerOnly(scheduler: HTTPRequestScheduler) - /// fail response before head received. scheduler and executor are exclusive here. - case cancel( - requestContinuation: CheckedContinuation, - scheduler: HTTPRequestScheduler?, - executor: HTTPRequestExecutor?, - bodyStreamContinuation: CheckedContinuation? - ) - } - - mutating func deadlineExceeded() -> DeadlineExceededAction { - let error = HTTPClientError.deadlineExceeded - switch self.state { - case .initialized(let continuation): - self.state = .finished(error: error) - return .cancel( - requestContinuation: continuation, - scheduler: nil, - executor: nil, - bodyStreamContinuation: nil - ) - - case .queued(let continuation, let scheduler): - self.state = .deadlineExceededWhileQueued(continuation) - return .cancelSchedulerOnly( - scheduler: scheduler - ) - case .deadlineExceededWhileQueued: - return .none - case .executing(let context, let requestStreamState, .waitingForResponseHead): - switch requestStreamState { - case .paused(continuation: .some(let continuation)): - self.state = .finished(error: error) - return .cancel( - requestContinuation: context.continuation, - scheduler: nil, - executor: context.executor, - bodyStreamContinuation: continuation - ) - case .requestHeadSent, .finished, .producing, .paused(continuation: .none): - self.state = .finished(error: error) - return .cancel( - requestContinuation: context.continuation, - scheduler: nil, - executor: context.executor, - bodyStreamContinuation: nil - ) - } - - case .executing, .finished: - // The user specified deadline is only used until we received the response head. - // If we already received the head, we have also resumed the continuation and - // therefore return the HTTPClientResponse to the user. We do not want to cancel - // the request body streaming nor the response body streaming afterwards. - return .none - } - } - } -} - -@available(*, unavailable) -extension Transaction.StateMachine: Sendable {} diff --git a/Sources/AsyncHTTPClient/AsyncAwait/Transaction.swift b/Sources/AsyncHTTPClient/AsyncAwait/Transaction.swift deleted file mode 100644 index 6bf8b38b7..000000000 --- a/Sources/AsyncHTTPClient/AsyncAwait/Transaction.swift +++ /dev/null @@ -1,356 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOHTTP1 -import NIOSSL - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -@usableFromInline -final class Transaction: - // until NIOLockedValueBox learns `sending` because StateMachine cannot be Sendable - @unchecked Sendable -{ - let logger: Logger - - let request: HTTPClientRequest.Prepared - - let connectionDeadline: NIODeadline - let preferredEventLoop: EventLoop - let requestOptions: RequestOptions - - private let state: NIOLockedValueBox - - init( - request: HTTPClientRequest.Prepared, - requestOptions: RequestOptions, - logger: Logger, - connectionDeadline: NIODeadline, - preferredEventLoop: EventLoop, - responseContinuation: CheckedContinuation - ) { - self.request = request - self.requestOptions = requestOptions - self.logger = logger - self.connectionDeadline = connectionDeadline - self.preferredEventLoop = preferredEventLoop - self.state = NIOLockedValueBox(StateMachine(responseContinuation)) - } - - func cancel() { - self.fail(CancellationError()) - } - - // MARK: Request body helpers - - private func writeOnceAndOneTimeOnly(byteBuffer: ByteBuffer) { - // This method is synchronously invoked after sending the request head. For this reason we - // can make a number of assumptions, how the state machine will react. - let writeAction = self.state.withLockedValue { state in - state.writeNextRequestPart() - } - - switch writeAction { - case .writeAndWait(let executor), .writeAndContinue(let executor): - executor.writeRequestBodyPart(.byteBuffer(byteBuffer), request: self, promise: nil) - - case .fail: - // an error/cancellation has happened. we don't need to continue here - return - } - - self.requestBodyStreamFinished() - } - - private func continueRequestBodyStream( - _ allocator: ByteBufferAllocator, - makeAsyncIterator: @Sendable @escaping () -> ((ByteBufferAllocator) async throws -> ByteBuffer?) - ) { - Task { - let next = makeAsyncIterator() - - do { - while let part = try await next(allocator) { - do { - try await self.writeRequestBodyPart(part) - } catch { - // If a write fails, the request has failed somewhere else. We must exit the - // write loop though. We don't need to report the error somewhere. - return - } - } - - self.requestBodyStreamFinished() - } catch { - // The only chance of reaching this catch block, is an error thrown in the `next` - // call above. - self.requestBodyStreamFailed(error) - } - } - } - - struct BreakTheWriteLoopError: Swift.Error {} - - // FIXME: Refactor this to not use `self.state.unsafe`. - private func writeRequestBodyPart(_ part: ByteBuffer) async throws { - self.state.unsafe.lock() - switch self.state.unsafe.withValueAssumingLockIsAcquired({ state in state.writeNextRequestPart() }) { - case .writeAndContinue(let executor): - self.state.unsafe.unlock() - executor.writeRequestBodyPart(.byteBuffer(part), request: self, promise: nil) - - case .writeAndWait(let executor): - try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in - self.state.unsafe.withValueAssumingLockIsAcquired({ state in - state.waitForRequestBodyDemand(continuation: continuation) - }) - self.state.unsafe.unlock() - - executor.writeRequestBodyPart(.byteBuffer(part), request: self, promise: nil) - } - - case .fail: - self.state.unsafe.unlock() - throw BreakTheWriteLoopError() - } - } - - private func requestBodyStreamFinished() { - let finishAction = self.state.withLockedValue { state in - state.finishRequestBodyStream() - } - - switch finishAction { - case .none: - // an error/cancellation has happened. nothing to do. - break - - case .forwardStreamFinished(let executor): - executor.finishRequestBodyStream(self, promise: nil) - } - return - } - - private func requestBodyStreamFailed(_ error: Error) { - self.fail(error) - } -} - -// MARK: - Protocol Methods - - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension Transaction: HTTPSchedulableRequest { - var poolKey: ConnectionPool.Key { self.request.poolKey } - var tlsConfiguration: TLSConfiguration? { self.request.tlsConfiguration } - var requiredEventLoop: EventLoop? { nil } - - func requestWasQueued(_ scheduler: HTTPRequestScheduler) { - self.state.withLockedValue { state in - state.requestWasQueued(scheduler) - } - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension Transaction: HTTPExecutableRequest { - var requestHead: HTTPRequestHead { self.request.head } - - var requestFramingMetadata: RequestFramingMetadata { self.request.requestFramingMetadata } - - // MARK: Request - - func willExecuteRequest(_ executor: HTTPRequestExecutor) { - let action = self.state.withLockedValue { state in - state.willExecuteRequest(executor) - } - - switch action { - case .cancel(let executor): - executor.cancelRequest(self) - case .cancelAndFail(let executor, let continuation, with: let error): - executor.cancelRequest(self) - continuation.resume(throwing: error) - case .none: - break - } - } - - func requestHeadSent() {} - - func resumeRequestBodyStream() { - let action = self.state.withLockedValue { state in - state.resumeRequestBodyStream() - } - - switch action { - case .none: - break - - case .startStream(let allocator): - switch self.request.body { - case .asyncSequence(_, let makeAsyncIterator): - // it is safe to call this async here. it dispatches... - self.continueRequestBodyStream(allocator, makeAsyncIterator: makeAsyncIterator) - - case .byteBuffer(let byteBuffer): - self.writeOnceAndOneTimeOnly(byteBuffer: byteBuffer) - - case .none: - break - - case .sequence(_, _, let create): - let byteBuffer = create(allocator) - self.writeOnceAndOneTimeOnly(byteBuffer: byteBuffer) - } - - case .resumeStream(let continuation): - continuation.resume(returning: ()) - } - } - - func pauseRequestBodyStream() { - self.state.withLockedValue { state in - state.pauseRequestBodyStream() - } - } - - // MARK: Response - - func receiveResponseHead(_ head: HTTPResponseHead) { - let action = self.state.withLockedValue { state in - state.receiveResponseHead(head, delegate: self) - } - - switch action { - case .none: - break - - case .succeedResponseHead(let body, let continuation): - let response = HTTPClientResponse( - requestMethod: self.requestHead.method, - version: head.version, - status: head.status, - headers: head.headers, - body: body, - history: [] - ) - continuation.resume(returning: response) - } - } - - func receiveResponseBodyParts(_ buffer: CircularBuffer) { - let action = self.state.withLockedValue { state in - state.receiveResponseBodyParts(buffer) - } - switch action { - case .none: - break - case .yieldResponseBodyParts(let source, let responseBodyParts, let executer): - switch source.yield(contentsOf: responseBodyParts) { - case .dropped, .stopProducing: - break - case .produceMore: - executer.demandResponseBodyStream(self) - } - } - } - - func succeedRequest(_ buffer: CircularBuffer?) { - let succeedAction = self.state.withLockedValue { state in - state.succeedRequest(buffer) - } - switch succeedAction { - case .finishResponseStream(let source, let finalResponse): - if let finalResponse = finalResponse { - _ = source.yield(contentsOf: finalResponse) - } - source.finish() - - case .none: - break - } - } - - func fail(_ error: Error) { - let action = self.state.withLockedValue { state in - state.fail(error) - } - self.performFailAction(action) - } - - private func performFailAction(_ action: StateMachine.FailAction) { - switch action { - case .none: - break - - case .failResponseHead(let continuation, let error, let scheduler, let executor, let bodyStreamContinuation): - continuation.resume(throwing: error) - bodyStreamContinuation?.resume(throwing: error) - scheduler?.cancelRequest(self) // NOTE: scheduler and executor are exclusive here - executor?.cancelRequest(self) - - case .failResponseStream(let source, let error, let executor, let requestBodyStreamContinuation): - source.finish(error) - requestBodyStreamContinuation?.resume(throwing: error) - executor.cancelRequest(self) - - case .failRequestStreamContinuation(let bodyStreamContinuation, let error): - bodyStreamContinuation.resume(throwing: error) - } - } - - func deadlineExceeded() { - let action = self.state.withLockedValue { state in - state.deadlineExceeded() - } - self.performDeadlineExceededAction(action) - } - - private func performDeadlineExceededAction(_ action: StateMachine.DeadlineExceededAction) { - switch action { - case .cancel(let requestContinuation, let scheduler, let executor, let bodyStreamContinuation): - requestContinuation.resume(throwing: HTTPClientError.deadlineExceeded) - scheduler?.cancelRequest(self) - executor?.cancelRequest(self) - bodyStreamContinuation?.resume(throwing: HTTPClientError.deadlineExceeded) - case .cancelSchedulerOnly(let scheduler): - scheduler.cancelRequest(self) - case .none: - break - } - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension Transaction: NIOAsyncSequenceProducerDelegate { - @usableFromInline - func produceMore() { - let action = self.state.withLockedValue { state in - state.produceMore() - } - switch action { - case .none: - break - case .requestMoreResponseBodyParts(let executer): - executer.demandResponseBodyStream(self) - } - } - - @usableFromInline - func didTerminate() { - self.fail(HTTPClientError.cancelled) - } -} diff --git a/Sources/AsyncHTTPClient/Base64.swift b/Sources/AsyncHTTPClient/Base64.swift deleted file mode 100644 index 4d2ddcc49..000000000 --- a/Sources/AsyncHTTPClient/Base64.swift +++ /dev/null @@ -1,174 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -// This is a simplified vendored version from: -// https://github.com/fabianfett/swift-base64-kit - -// swiftformat:disable all - -extension String { - - /// Base64 encode a collection of UInt8 to a string, without the use of Foundation. - @inlinable - init(base64Encoding bytes: Buffer) - where Buffer.Element == UInt8 { - self = Base64.encode(bytes: bytes) - } -} - -// swift-format-ignore: DontRepeatTypeInStaticProperties -@usableFromInline -internal struct Base64: Sendable { - - @inlinable - static func encode( - bytes: Buffer - ) - -> String where Buffer.Element == UInt8 - { - guard !bytes.isEmpty else { - return "" - } - // In Base64, 3 bytes become 4 output characters, and we pad to the - // nearest multiple of four. - let base64StringLength = ((bytes.count + 2) / 3) * 4 - let alphabet = Base64.encodeBase64 - - return String(customUnsafeUninitializedCapacity: base64StringLength) { backingStorage in - var input = bytes.makeIterator() - var offset = 0 - while let firstByte = input.next() { - let secondByte = input.next() - let thirdByte = input.next() - - backingStorage[offset] = Base64.encode(alphabet: alphabet, firstByte: firstByte) - backingStorage[offset + 1] = Base64.encode( - alphabet: alphabet, - firstByte: firstByte, - secondByte: secondByte - ) - backingStorage[offset + 2] = Base64.encode( - alphabet: alphabet, - secondByte: secondByte, - thirdByte: thirdByte - ) - backingStorage[offset + 3] = Base64.encode(alphabet: alphabet, thirdByte: thirdByte) - offset += 4 - } - return offset - } - } - - // MARK: Internal - - // The base64 unicode table. - @usableFromInline - static let encodeBase64: [UInt8] = [ - UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "D"), - UInt8(ascii: "E"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "H"), - UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), UInt8(ascii: "L"), - UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"), - UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"), - UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "X"), - UInt8(ascii: "Y"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "b"), - UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f"), - UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"), - UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"), - UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "r"), - UInt8(ascii: "s"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "v"), - UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), UInt8(ascii: "z"), - UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), - UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), - UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "+"), UInt8(ascii: "/"), - ] - - static let encodePaddingCharacter: UInt8 = UInt8(ascii: "=") - - @usableFromInline - static func encode(alphabet: [UInt8], firstByte: UInt8) -> UInt8 { - let index = firstByte >> 2 - return alphabet[Int(index)] - } - - @usableFromInline - static func encode(alphabet: [UInt8], firstByte: UInt8, secondByte: UInt8?) -> UInt8 { - var index = (firstByte & 0b00000011) << 4 - if let secondByte = secondByte { - index += (secondByte & 0b11110000) >> 4 - } - return alphabet[Int(index)] - } - - @usableFromInline - static func encode(alphabet: [UInt8], secondByte: UInt8?, thirdByte: UInt8?) -> UInt8 { - guard let secondByte = secondByte else { - // No second byte means we are just emitting padding. - return Base64.encodePaddingCharacter - } - var index = (secondByte & 0b00001111) << 2 - if let thirdByte = thirdByte { - index += (thirdByte & 0b11000000) >> 6 - } - return alphabet[Int(index)] - } - - @usableFromInline - static func encode(alphabet: [UInt8], thirdByte: UInt8?) -> UInt8 { - guard let thirdByte = thirdByte else { - // No third byte means just padding. - return Base64.encodePaddingCharacter - } - let index = thirdByte & 0b00111111 - return alphabet[Int(index)] - } -} - -extension String { - /// This is a backport of a proposed String initializer that will allow writing directly into an uninitialized String's backing memory. - /// - /// As this API does not exist prior to 5.3 on Linux, or on older Apple platforms, we fake it out with a pointer and accept the extra copy. - @inlinable - init( - backportUnsafeUninitializedCapacity capacity: Int, - initializingUTF8With initializer: (_ buffer: UnsafeMutableBufferPointer) throws -> Int - ) rethrows { - // The buffer will store zero terminated C string - let buffer = UnsafeMutableBufferPointer.allocate(capacity: capacity + 1) - defer { - buffer.deallocate() - } - - let initializedCount = try initializer(buffer) - precondition(initializedCount <= capacity, "Overran buffer in initializer!") - // add zero termination - buffer[initializedCount] = 0 - - self = String(cString: buffer.baseAddress!) - } -} - -extension String { - - @inlinable - init( - customUnsafeUninitializedCapacity capacity: Int, - initializingUTF8With initializer: (_ buffer: UnsafeMutableBufferPointer) throws -> Int - ) rethrows { - if #available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) { - try self.init(unsafeUninitializedCapacity: capacity, initializingUTF8With: initializer) - } else { - try self.init(backportUnsafeUninitializedCapacity: capacity, initializingUTF8With: initializer) - } - } -} diff --git a/Sources/AsyncHTTPClient/BasicAuth.swift b/Sources/AsyncHTTPClient/BasicAuth.swift deleted file mode 100644 index 3e69f8277..000000000 --- a/Sources/AsyncHTTPClient/BasicAuth.swift +++ /dev/null @@ -1,39 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2024 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation -import NIOHTTP1 - -/// Generates base64 encoded username + password for http basic auth. -/// -/// - Parameters: -/// - username: the username to authenticate with -/// - password: authentication password associated with the username -/// - Returns: encoded credentials to use the Authorization: Basic http header. -func encodeBasicAuthCredentials(username: String, password: String) -> String { - var value = Data() - value.reserveCapacity(username.utf8.count + password.utf8.count + 1) - value.append(contentsOf: username.utf8) - value.append(UInt8(ascii: ":")) - value.append(contentsOf: password.utf8) - return value.base64EncodedString() -} - -extension HTTPHeaders { - /// Sets the basic auth header - mutating func setBasicAuth(username: String, password: String) { - let encoded = encodeBasicAuthCredentials(username: username, password: password) - self.replaceOrAdd(name: "Authorization", value: "Basic \(encoded)") - } -} diff --git a/Sources/AsyncHTTPClient/BestEffortHashableTLSConfiguration.swift b/Sources/AsyncHTTPClient/BestEffortHashableTLSConfiguration.swift deleted file mode 100644 index aca0ce235..000000000 --- a/Sources/AsyncHTTPClient/BestEffortHashableTLSConfiguration.swift +++ /dev/null @@ -1,32 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOSSL - -/// Wrapper around `TLSConfiguration` from NIOSSL to provide a best effort implementation of `Hashable` -struct BestEffortHashableTLSConfiguration: Hashable { - let base: TLSConfiguration - - init(wrapping base: TLSConfiguration) { - self.base = base - } - - func hash(into hasher: inout Hasher) { - self.base.bestEffortHash(into: &hasher) - } - - static func == (lhs: BestEffortHashableTLSConfiguration, rhs: BestEffortHashableTLSConfiguration) -> Bool { - lhs.base.bestEffortEquals(rhs.base) - } -} diff --git a/Sources/AsyncHTTPClient/Configuration+BrowserLike.swift b/Sources/AsyncHTTPClient/Configuration+BrowserLike.swift deleted file mode 100644 index 5a0abdfad..000000000 --- a/Sources/AsyncHTTPClient/Configuration+BrowserLike.swift +++ /dev/null @@ -1,45 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2023 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// -import NIOCore -import NIOHTTPCompression -import NIOSSL - -// swift-format-ignore: DontRepeatTypeInStaticProperties -extension HTTPClient.Configuration { - /// The ``HTTPClient/Configuration`` for ``HTTPClient/shared`` which tries to mimic the platform's default or prevalent browser as closely as possible. - /// - /// Don't rely on specific values of this configuration as they're subject to change. You can rely on them being somewhat sensible though. - /// - /// - note: At present, this configuration is nowhere close to a real browser configuration but in case of disagreements we will choose values that match - /// the default browser as closely as possible. - /// - /// Platform's default/prevalent browsers that we're trying to match (these might change over time): - /// - macOS: Safari - /// - iOS: Safari - /// - Android: Google Chrome - /// - Linux (non-Android): Google Chrome - public static var singletonConfiguration: HTTPClient.Configuration { - // To start with, let's go with these values. Obtained from Firefox's config. - HTTPClient.Configuration( - certificateVerification: .fullVerification, - redirectConfiguration: .follow(max: 20, allowCycles: false), - timeout: Timeout(connect: .seconds(90), read: .seconds(90)), - connectionPool: .seconds(600), - proxy: nil, - ignoreUncleanSSLShutdown: false, - decompression: .enabled(limit: .ratio(25)), - backgroundActivityLogger: nil - ) - } -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool.swift b/Sources/AsyncHTTPClient/ConnectionPool.swift deleted file mode 100644 index b5b058c2e..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool.swift +++ /dev/null @@ -1,119 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2019-2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import CNIOLinux -import NIOCore -import NIOSSL - -#if canImport(Darwin) -import Darwin.C -#elseif canImport(Musl) -import Musl -#elseif canImport(Android) -import Android -#elseif os(Linux) || os(FreeBSD) -import Glibc -#else -#error("unsupported target operating system") -#endif - -extension String { - var isIPAddress: Bool { - var ipv4Address = in_addr() - var ipv6Address = in6_addr() - return self.withCString { host in - inet_pton(AF_INET, host, &ipv4Address) == 1 || inet_pton(AF_INET6, host, &ipv6Address) == 1 - } - } -} - -enum ConnectionPool { - /// Used by the `ConnectionPool` to index its `HTTP1ConnectionProvider`s - /// - /// A key is initialized from a `Request`, it uses the components to derive a hashed value - /// used by the `providers` dictionary to allow retrieving and creating - /// connection providers associated to a certain request in constant time. - struct Key: Hashable, CustomStringConvertible { - var scheme: Scheme - var connectionTarget: ConnectionTarget - private var tlsConfiguration: BestEffortHashableTLSConfiguration? - var serverNameIndicatorOverride: String? - - init( - scheme: Scheme, - connectionTarget: ConnectionTarget, - tlsConfiguration: BestEffortHashableTLSConfiguration? = nil, - serverNameIndicatorOverride: String? - ) { - self.scheme = scheme - self.connectionTarget = connectionTarget - self.tlsConfiguration = tlsConfiguration - self.serverNameIndicatorOverride = serverNameIndicatorOverride - } - - var description: String { - var hasher = Hasher() - self.tlsConfiguration?.hash(into: &hasher) - let hash = hasher.finalize() - let hostDescription: String - switch self.connectionTarget { - case .ipAddress(let serialization, let addr): - hostDescription = "\(serialization):\(addr.port!)" - case .domain(let domain, let port): - hostDescription = "\(domain):\(port)" - case .unixSocket(let socketPath): - hostDescription = socketPath - } - return - "\(self.scheme)://\(hostDescription)\(self.serverNameIndicatorOverride.map { " SNI: \($0)" } ?? "") TLS-hash: \(hash) " - } - } -} - -extension DeconstructedURL { - func applyDNSOverride(_ dnsOverride: [String: String]) -> (ConnectionTarget, serverNameIndicatorOverride: String?) { - guard - let originalHost = self.connectionTarget.host, - let hostOverride = dnsOverride[originalHost] - else { - return (self.connectionTarget, nil) - } - return ( - .init(remoteHost: hostOverride, port: self.connectionTarget.port ?? self.scheme.defaultPort), - serverNameIndicatorOverride: originalHost.isIPAddress ? nil : originalHost - ) - } -} - -extension ConnectionPool.Key { - init(url: DeconstructedURL, tlsConfiguration: TLSConfiguration?, dnsOverride: [String: String]) { - let (connectionTarget, serverNameIndicatorOverride) = url.applyDNSOverride(dnsOverride) - self.init( - scheme: url.scheme, - connectionTarget: connectionTarget, - tlsConfiguration: tlsConfiguration.map { - BestEffortHashableTLSConfiguration(wrapping: $0) - }, - serverNameIndicatorOverride: serverNameIndicatorOverride - ) - } - - init(_ request: HTTPClient.Request, dnsOverride: [String: String] = [:]) { - self.init( - url: request.deconstructedURL, - tlsConfiguration: request.tlsConfiguration, - dnsOverride: dnsOverride - ) - } -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/ChannelHandler/HTTP1ProxyConnectHandler.swift b/Sources/AsyncHTTPClient/ConnectionPool/ChannelHandler/HTTP1ProxyConnectHandler.swift deleted file mode 100644 index 1636fe379..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/ChannelHandler/HTTP1ProxyConnectHandler.swift +++ /dev/null @@ -1,227 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOHTTP1 - -final class HTTP1ProxyConnectHandler: ChannelDuplexHandler, RemovableChannelHandler { - typealias OutboundIn = Never - typealias OutboundOut = HTTPClientRequestPart - typealias InboundIn = HTTPClientResponsePart - - enum State { - // transitions to `.connectSent` or `.failed` - case initialized - // transitions to `.headReceived` or `.failed` - case connectSent(Scheduled) - // transitions to `.completed` or `.failed` - case headReceived(Scheduled) - // final error state - case failed(Error) - // final success state - case completed - } - - private var state: State = .initialized - - private let targetHost: String - private let targetPort: Int - private let proxyAuthorization: HTTPClient.Authorization? - private let deadline: NIODeadline - - private var proxyEstablishedPromise: EventLoopPromise? - var proxyEstablishedFuture: EventLoopFuture? { - self.proxyEstablishedPromise?.futureResult - } - - convenience init( - target: ConnectionTarget, - proxyAuthorization: HTTPClient.Authorization?, - deadline: NIODeadline - ) { - let targetHost: String - let targetPort: Int - switch target { - case .ipAddress(let serialization, let address): - targetHost = serialization - targetPort = address.port! - case .domain(name: let domain, let port): - targetHost = domain - targetPort = port - case .unixSocket: - fatalError("Unix Domain Sockets do not support proxies") - } - self.init( - targetHost: targetHost, - targetPort: targetPort, - proxyAuthorization: proxyAuthorization, - deadline: deadline - ) - } - - init( - targetHost: String, - targetPort: Int, - proxyAuthorization: HTTPClient.Authorization?, - deadline: NIODeadline - ) { - self.targetHost = targetHost - self.targetPort = targetPort - self.proxyAuthorization = proxyAuthorization - self.deadline = deadline - } - - func handlerAdded(context: ChannelHandlerContext) { - self.proxyEstablishedPromise = context.eventLoop.makePromise(of: Void.self) - - self.sendConnect(context: context) - } - - func handlerRemoved(context: ChannelHandlerContext) { - switch self.state { - case .failed, .completed: - break - case .initialized, .connectSent, .headReceived: - struct NoResult: Error {} - self.state = .failed(NoResult()) - self.proxyEstablishedPromise?.fail(NoResult()) - } - } - - func channelActive(context: ChannelHandlerContext) { - self.sendConnect(context: context) - } - - func channelInactive(context: ChannelHandlerContext) { - switch self.state { - case .initialized: - preconditionFailure("How can we receive a channelInactive before a channelActive?") - case .connectSent(let timeout), .headReceived(let timeout): - timeout.cancel() - self.failWithError(HTTPClientError.remoteConnectionClosed, context: context, closeConnection: false) - - case .failed, .completed: - break - } - } - - func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise?) { - preconditionFailure("We don't support outgoing traffic during HTTP Proxy update.") - } - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - switch self.unwrapInboundIn(data) { - case .head(let head): - self.handleHTTPHeadReceived(head, context: context) - case .body: - self.handleHTTPBodyReceived(context: context) - case .end: - self.handleHTTPEndReceived(context: context) - } - } - - private func sendConnect(context: ChannelHandlerContext) { - guard case .initialized = self.state else { - // we might run into this handler twice, once in handlerAdded and once in channelActive. - return - } - - let timeout = context.eventLoop.assumeIsolated().scheduleTask(deadline: self.deadline) { - switch self.state { - case .initialized: - preconditionFailure("How can we have a scheduled timeout, if the connection is not even up?") - - case .connectSent, .headReceived: - self.failWithError(HTTPClientError.httpProxyHandshakeTimeout, context: context) - - case .failed, .completed: - break - } - } - - self.state = .connectSent(timeout) - - var head = HTTPRequestHead( - version: .init(major: 1, minor: 1), - method: .CONNECT, - uri: "\(self.targetHost):\(self.targetPort)" - ) - head.headers.replaceOrAdd(name: "host", value: "\(self.targetHost)") - if let authorization = self.proxyAuthorization { - head.headers.replaceOrAdd(name: "proxy-authorization", value: authorization.headerValue) - } - context.write(self.wrapOutboundOut(.head(head)), promise: nil) - context.write(self.wrapOutboundOut(.end(nil)), promise: nil) - context.flush() - } - - private func handleHTTPHeadReceived(_ head: HTTPResponseHead, context: ChannelHandlerContext) { - guard case .connectSent(let scheduled) = self.state else { - preconditionFailure("HTTPDecoder should throw an error, if we have not send a request") - } - - switch head.status.code { - case 200..<300: - // Any 2xx (Successful) response indicates that the sender (and all - // inbound proxies) will switch to tunnel mode immediately after the - // blank line that concludes the successful response's header section - self.state = .headReceived(scheduled) - case 407: - self.failWithError(HTTPClientError.proxyAuthenticationRequired, context: context) - - default: - // Any response other than a successful response indicates that the tunnel - // has not yet been formed and that the connection remains governed by HTTP. - self.failWithError(HTTPClientError.invalidProxyResponse, context: context) - } - } - - private func handleHTTPBodyReceived(context: ChannelHandlerContext) { - switch self.state { - case .headReceived(let timeout): - timeout.cancel() - // we don't expect a body - self.failWithError(HTTPClientError.invalidProxyResponse, context: context) - case .failed: - // ran into an error before... ignore this one - break - case .completed, .connectSent, .initialized: - preconditionFailure("Invalid state: \(self.state)") - } - } - - private func handleHTTPEndReceived(context: ChannelHandlerContext) { - switch self.state { - case .headReceived(let timeout): - timeout.cancel() - self.state = .completed - self.proxyEstablishedPromise?.succeed(()) - - case .failed: - // ran into an error before... ignore this one - break - case .initialized, .connectSent, .completed: - preconditionFailure("Invalid state: \(self.state)") - } - } - - private func failWithError(_ error: Error, context: ChannelHandlerContext, closeConnection: Bool = true) { - self.state = .failed(error) - self.proxyEstablishedPromise?.fail(error) - context.fireErrorCaught(error) - if closeConnection { - context.close(mode: .all, promise: nil) - } - } -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/ChannelHandler/SOCKSEventsHandler.swift b/Sources/AsyncHTTPClient/ConnectionPool/ChannelHandler/SOCKSEventsHandler.swift deleted file mode 100644 index 7458627fd..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/ChannelHandler/SOCKSEventsHandler.swift +++ /dev/null @@ -1,117 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOSOCKS - -final class SOCKSEventsHandler: ChannelInboundHandler, RemovableChannelHandler { - typealias InboundIn = NIOAny - - enum State { - // transitions to channelActive or failed - case initialized - // transitions to socksEstablished or failed - case channelActive(Scheduled) - // final success state - case socksEstablished - // final success state - case failed(Error) - } - - private var socksEstablishedPromise: EventLoopPromise? - var socksEstablishedFuture: EventLoopFuture? { - self.socksEstablishedPromise?.futureResult - } - - private let deadline: NIODeadline - private var state: State = .initialized - - init(deadline: NIODeadline) { - self.deadline = deadline - } - - func handlerAdded(context: ChannelHandlerContext) { - self.socksEstablishedPromise = context.eventLoop.makePromise(of: Void.self) - - if context.channel.isActive { - self.connectionStarted(context: context) - } - } - - func handlerRemoved(context: ChannelHandlerContext) { - struct NoResult: Error {} - self.socksEstablishedPromise!.fail(NoResult()) - } - - func channelActive(context: ChannelHandlerContext) { - self.connectionStarted(context: context) - } - - func userInboundEventTriggered(context: ChannelHandlerContext, event: Any) { - guard event is SOCKSProxyEstablishedEvent else { - return context.fireUserInboundEventTriggered(event) - } - - switch self.state { - case .initialized: - preconditionFailure("How can we establish a SOCKS connection, if we are not connected?") - case .socksEstablished: - preconditionFailure("`SOCKSProxyEstablishedEvent` must only be fired once.") - case .channelActive(let scheduled): - self.state = .socksEstablished - scheduled.cancel() - self.socksEstablishedPromise?.succeed(()) - context.fireUserInboundEventTriggered(event) - case .failed: - // potentially a race with the timeout... - break - } - } - - func errorCaught(context: ChannelHandlerContext, error: Error) { - switch self.state { - case .initialized: - self.state = .failed(error) - self.socksEstablishedPromise?.fail(error) - case .channelActive(let scheduled): - scheduled.cancel() - self.state = .failed(error) - self.socksEstablishedPromise?.fail(error) - case .socksEstablished, .failed: - break - } - context.fireErrorCaught(error) - } - - private func connectionStarted(context: ChannelHandlerContext) { - guard case .initialized = self.state else { - return - } - - let scheduled = context.eventLoop.assumeIsolated().scheduleTask(deadline: self.deadline) { - switch self.state { - case .initialized, .channelActive: - // close the connection, if the handshake timed out - context.close(mode: .all, promise: nil) - let error = HTTPClientError.socksHandshakeTimeout - self.state = .failed(error) - self.socksEstablishedPromise?.fail(error) - case .failed, .socksEstablished: - break - } - } - - self.state = .channelActive(scheduled) - } -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/ChannelHandler/TLSEventsHandler.swift b/Sources/AsyncHTTPClient/ConnectionPool/ChannelHandler/TLSEventsHandler.swift deleted file mode 100644 index d210b2747..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/ChannelHandler/TLSEventsHandler.swift +++ /dev/null @@ -1,123 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOTLS - -final class TLSEventsHandler: ChannelInboundHandler, RemovableChannelHandler { - typealias InboundIn = NIOAny - - enum State { - // transitions to channelActive or failed - case initialized - // transitions to tlsEstablished or failed - case channelActive(Scheduled?) - // final success state - case tlsEstablished - // final success state - case failed(Error) - } - - private var tlsEstablishedPromise: EventLoopPromise? - var tlsEstablishedFuture: EventLoopFuture? { - self.tlsEstablishedPromise?.futureResult - } - - private let deadline: NIODeadline? - private var state: State = .initialized - - init(deadline: NIODeadline?) { - self.deadline = deadline - } - - func handlerAdded(context: ChannelHandlerContext) { - self.tlsEstablishedPromise = context.eventLoop.makePromise(of: String?.self) - - if context.channel.isActive { - self.connectionStarted(context: context) - } - } - - func handlerRemoved(context: ChannelHandlerContext) { - struct NoResult: Error {} - self.tlsEstablishedPromise!.fail(NoResult()) - } - - func channelActive(context: ChannelHandlerContext) { - self.connectionStarted(context: context) - } - - func userInboundEventTriggered(context: ChannelHandlerContext, event: Any) { - guard let tlsEvent = event as? TLSUserEvent else { - return context.fireUserInboundEventTriggered(event) - } - - switch tlsEvent { - case .handshakeCompleted(negotiatedProtocol: let negotiated): - switch self.state { - case .initialized: - preconditionFailure("How can we establish a TLS connection, if we are not connected?") - case .channelActive(let scheduled): - self.state = .tlsEstablished - scheduled?.cancel() - self.tlsEstablishedPromise?.succeed(negotiated) - context.fireUserInboundEventTriggered(event) - case .tlsEstablished, .failed: - // potentially a race with the timeout... - break - } - case .shutdownCompleted: - break - } - } - - func errorCaught(context: ChannelHandlerContext, error: Error) { - switch self.state { - case .initialized: - self.state = .failed(error) - self.tlsEstablishedPromise?.fail(error) - case .channelActive(let scheduled): - scheduled?.cancel() - self.state = .failed(error) - self.tlsEstablishedPromise?.fail(error) - case .tlsEstablished, .failed: - break - } - context.fireErrorCaught(error) - } - - private func connectionStarted(context: ChannelHandlerContext) { - guard case .initialized = self.state else { - return - } - - var scheduled: Scheduled? - if let deadline = deadline { - scheduled = context.eventLoop.assumeIsolated().scheduleTask(deadline: deadline) { - switch self.state { - case .initialized, .channelActive: - // close the connection, if the handshake timed out - context.close(mode: .all, promise: nil) - let error = HTTPClientError.tlsHandshakeTimeout - self.state = .failed(error) - self.tlsEstablishedPromise?.fail(error) - case .failed, .tlsEstablished: - break - } - } - } - - self.state = .channelActive(scheduled) - } -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/HTTP1/HTTP1ClientChannelHandler.swift b/Sources/AsyncHTTPClient/ConnectionPool/HTTP1/HTTP1ClientChannelHandler.swift deleted file mode 100644 index 191517c71..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/HTTP1/HTTP1ClientChannelHandler.swift +++ /dev/null @@ -1,716 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOCore -import NIOHTTP1 - -final class HTTP1ClientChannelHandler: ChannelDuplexHandler { - typealias OutboundIn = HTTPExecutableRequest - typealias OutboundOut = HTTPClientRequestPart - typealias InboundIn = HTTPClientResponsePart - - private var state: HTTP1ConnectionStateMachine = .init() { - didSet { - self.eventLoop.assertInEventLoop() - } - } - - /// while we are in a channel pipeline, this context can be used. - private var channelContext: ChannelHandlerContext? - - /// the currently executing request - private var request: HTTPExecutableRequest? { - didSet { - if let newRequest = self.request { - var requestLogger = newRequest.logger - requestLogger[metadataKey: "ahc-connection-id"] = self.connectionIdLoggerMetadata - requestLogger[metadataKey: "ahc-el"] = self.eventLoopDescription - self.logger = requestLogger - - if let idleReadTimeout = newRequest.requestOptions.idleReadTimeout { - self.idleReadTimeoutStateMachine = .init(timeAmount: idleReadTimeout) - } - - if let idleWriteTimeout = newRequest.requestOptions.idleWriteTimeout { - self.idleWriteTimeoutStateMachine = .init( - timeAmount: idleWriteTimeout, - isWritabilityEnabled: self.channelContext?.channel.isWritable ?? false - ) - } - } else { - self.logger = self.backgroundLogger - self.idleReadTimeoutStateMachine = nil - self.idleWriteTimeoutStateMachine = nil - } - } - } - - private var idleReadTimeoutStateMachine: IdleReadStateMachine? - private var idleReadTimeoutTimer: Scheduled? - - private var idleWriteTimeoutStateMachine: IdleWriteStateMachine? - private var idleWriteTimeoutTimer: Scheduled? - - /// Cancelling a task in NIO does *not* guarantee that the task will not execute under certain race conditions. - /// We therefore give each timer an ID and increase the ID every time we reset or cancel it. - /// We check in the task if the timer ID has changed in the meantime and do not execute any action if has changed. - private var currentIdleReadTimeoutTimerID: Int = 0 - private var currentIdleWriteTimeoutTimerID: Int = 0 - - private let backgroundLogger: Logger - private var logger: Logger - private let eventLoop: EventLoop - private let eventLoopDescription: Logger.MetadataValue - private let connectionIdLoggerMetadata: Logger.MetadataValue - - var onConnectionIdle: () -> Void = {} - init(eventLoop: EventLoop, backgroundLogger: Logger, connectionIdLoggerMetadata: Logger.MetadataValue) { - self.eventLoop = eventLoop - self.eventLoopDescription = "\(eventLoop.description)" - self.backgroundLogger = backgroundLogger - self.logger = backgroundLogger - self.connectionIdLoggerMetadata = connectionIdLoggerMetadata - } - - func handlerAdded(context: ChannelHandlerContext) { - self.channelContext = context - - if context.channel.isActive { - let action = self.state.channelActive(isWritable: context.channel.isWritable) - self.run(action, context: context) - } - } - - func handlerRemoved(context: ChannelHandlerContext) { - self.channelContext = nil - } - - // MARK: Channel Inbound Handler - - func channelActive(context: ChannelHandlerContext) { - self.logger.trace( - "Channel active", - metadata: [ - "ahc-channel-writable": "\(context.channel.isWritable)" - ] - ) - - let action = self.state.channelActive(isWritable: context.channel.isWritable) - self.run(action, context: context) - } - - func channelInactive(context: ChannelHandlerContext) { - self.logger.trace("Channel inactive") - - let action = self.state.channelInactive() - self.run(action, context: context) - } - - func channelWritabilityChanged(context: ChannelHandlerContext) { - self.logger.trace( - "Channel writability changed", - metadata: [ - "ahc-channel-writable": "\(context.channel.isWritable)" - ] - ) - - if let timeoutAction = self.idleWriteTimeoutStateMachine?.channelWritabilityChanged(context: context) { - self.runTimeoutAction(timeoutAction, context: context) - } - - let action = self.state.writabilityChanged(writable: context.channel.isWritable) - self.run(action, context: context) - context.fireChannelWritabilityChanged() - } - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - let httpPart = self.unwrapInboundIn(data) - - self.logger.trace( - "HTTP response part received", - metadata: [ - "ahc-http-part": "\(httpPart)" - ] - ) - - if let timeoutAction = self.idleReadTimeoutStateMachine?.channelRead(httpPart) { - self.runTimeoutAction(timeoutAction, context: context) - } - - let action = self.state.channelRead(httpPart) - self.run(action, context: context) - } - - func channelReadComplete(context: ChannelHandlerContext) { - self.logger.trace("Channel read complete caught") - - let action = self.state.channelReadComplete() - self.run(action, context: context) - } - - func errorCaught(context: ChannelHandlerContext, error: Error) { - self.logger.trace( - "Channel error caught", - metadata: [ - "ahc-error": "\(error)" - ] - ) - - let action = self.state.errorHappened(error) - self.run(action, context: context) - } - - // MARK: Channel Outbound Handler - - func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise?) { - assert(self.request == nil, "Only write to the ChannelHandler if you are sure, it is idle!") - let req = self.unwrapOutboundIn(data) - self.request = req - - self.logger.debug("Request was scheduled on connection") - - if let timeoutAction = self.idleWriteTimeoutStateMachine?.write() { - self.runTimeoutAction(timeoutAction, context: context) - } - - req.willExecuteRequest(self.requestExecutor) - - let action = self.state.runNewRequest( - head: req.requestHead, - metadata: req.requestFramingMetadata - ) - self.run(action, context: context) - } - - func read(context: ChannelHandlerContext) { - self.logger.trace("Read event caught") - - let action = self.state.read() - self.run(action, context: context) - } - - func triggerUserOutboundEvent(context: ChannelHandlerContext, event: Any, promise: EventLoopPromise?) { - switch event { - case HTTPConnectionEvent.shutdownRequested: - self.logger.trace("User outbound event triggered: Cancel request for connection close") - let action = self.state.requestCancelled(closeConnection: true) - self.run(action, context: context) - default: - context.fireUserInboundEventTriggered(event) - } - } - - // MARK: - Private Methods - - - // MARK: Run Actions - - private func run(_ action: HTTP1ConnectionStateMachine.Action, context: ChannelHandlerContext) { - switch action { - case .sendRequestHead(let head, let sendEnd): - self.sendRequestHead(head, sendEnd: sendEnd, context: context) - case .notifyRequestHeadSendSuccessfully(let resumeRequestBodyStream, let startIdleTimer): - // We can force unwrap the request here, as we have just validated in the state machine, - // that the request is neither failed nor finished yet - self.request!.requestHeadSent() - if resumeRequestBodyStream, let request = self.request { - // The above request head send notification might lead the request to mark itself as - // cancelled, which in turn might pop the request of the handler. For this reason we - // must check if the request is still present here. - request.resumeRequestBodyStream() - } - if startIdleTimer { - if let readTimeoutAction = self.idleReadTimeoutStateMachine?.requestEndSent() { - self.runTimeoutAction(readTimeoutAction, context: context) - } - - if let writeTimeoutAction = self.idleWriteTimeoutStateMachine?.requestEndSent() { - self.runTimeoutAction(writeTimeoutAction, context: context) - } - } - case .sendBodyPart(let part, let writePromise): - context.writeAndFlush(self.wrapOutboundOut(.body(part)), promise: writePromise) - - case .sendRequestEnd(let writePromise): - context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: writePromise) - - if let readTimeoutAction = self.idleReadTimeoutStateMachine?.requestEndSent() { - self.runTimeoutAction(readTimeoutAction, context: context) - } - - if let writeTimeoutAction = self.idleWriteTimeoutStateMachine?.requestEndSent() { - self.runTimeoutAction(writeTimeoutAction, context: context) - } - - case .pauseRequestBodyStream: - // We can force unwrap the request here, as we have just validated in the state machine, - // that the request is neither failed nor finished yet - self.request!.pauseRequestBodyStream() - - case .resumeRequestBodyStream: - // We can force unwrap the request here, as we have just validated in the state machine, - // that the request is neither failed nor finished yet - self.request!.resumeRequestBodyStream() - - case .fireChannelActive: - context.fireChannelActive() - - case .fireChannelInactive: - context.fireChannelInactive() - - case .fireChannelError(let error, let close): - context.fireErrorCaught(error) - if close { - context.close(promise: nil) - } - - case .read: - context.read() - - case .close: - context.close(promise: nil) - - case .wait: - break - - case .forwardResponseHead(let head, let pauseRequestBodyStream): - // We can force unwrap the request here, as we have just validated in the state machine, - // that the request is neither failed nor finished yet - self.request!.receiveResponseHead(head) - if pauseRequestBodyStream, let request = self.request { - // The above response head forward might lead the request to mark itself as - // cancelled, which in turn might pop the request of the handler. For this reason we - // must check if the request is still present here. - request.pauseRequestBodyStream() - } - - case .forwardResponseBodyParts(let buffer): - // We can force unwrap the request here, as we have just validated in the state machine, - // that the request is neither failed nor finished yet - self.request!.receiveResponseBodyParts(buffer) - - case .succeedRequest(let finalAction, let buffer): - // We can force unwrap the request here, as we have just validated in the state machine, - // that the request is neither failed nor finished yet - - // The order here is very important... - // We first nil our own task property! `taskCompleted` will potentially lead to - // situations in which we get a new request right away. We should finish the task - // after the connection was notified, that we finished. A - // `HTTPClient.shutdown(requiresCleanShutdown: true)` will fail if we do it the - // other way around. - - let oldRequest = self.request! - self.request = nil - self.runTimeoutAction(.clearIdleReadTimeoutTimer, context: context) - self.runTimeoutAction(.clearIdleWriteTimeoutTimer, context: context) - - switch finalAction { - case .close: - context.close(promise: nil) - oldRequest.succeedRequest(buffer) - case .sendRequestEnd(let writePromise, let shouldClose): - let writePromise = writePromise ?? context.eventLoop.makePromise(of: Void.self) - // We need to defer succeeding the old request to avoid ordering issues - writePromise.futureResult.hop(to: context.eventLoop).assumeIsolated().whenComplete { result in - switch result { - case .success: - // If our final action was `sendRequestEnd`, that means we've already received - // the complete response. As a result, once we've uploaded all the body parts - // we need to tell the pool that the connection is idle or, if we were asked to - // close when we're done, send the close. Either way, we then succeed the request - if shouldClose { - context.close(promise: nil) - } else { - self.onConnectionIdle() - } - - oldRequest.succeedRequest(buffer) - case .failure(let error): - context.close(promise: nil) - oldRequest.fail(error) - } - } - - context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: writePromise) - case .informConnectionIsIdle: - self.onConnectionIdle() - oldRequest.succeedRequest(buffer) - } - - case .failRequest(let error, let finalAction): - // see comment in the `succeedRequest` case. - let oldRequest = self.request! - self.request = nil - self.runTimeoutAction(.clearIdleReadTimeoutTimer, context: context) - self.runTimeoutAction(.clearIdleWriteTimeoutTimer, context: context) - - switch finalAction { - case .close(let writePromise): - context.close(promise: nil) - writePromise?.fail(error) - oldRequest.fail(error) - - case .informConnectionIsIdle: - self.onConnectionIdle() - oldRequest.fail(error) - - case .failWritePromise(let writePromise): - writePromise?.fail(error) - oldRequest.fail(error) - - case .none: - oldRequest.fail(error) - } - - case .failSendBodyPart(let error, let writePromise), .failSendStreamFinished(let error, let writePromise): - writePromise?.fail(error) - } - } - - private func sendRequestHead(_ head: HTTPRequestHead, sendEnd: Bool, context: ChannelHandlerContext) { - if sendEnd { - context.write(self.wrapOutboundOut(.head(head)), promise: nil) - context.write(self.wrapOutboundOut(.end(nil)), promise: nil) - context.flush() - } else { - context.writeAndFlush(self.wrapOutboundOut(.head(head)), promise: nil) - } - self.run(self.state.headSent(), context: context) - } - - private func runTimeoutAction(_ action: IdleReadStateMachine.Action, context: ChannelHandlerContext) { - switch action { - case .startIdleReadTimeoutTimer(let timeAmount): - assert(self.idleReadTimeoutTimer == nil, "Expected there is no timeout timer so far.") - - let timerID = self.currentIdleReadTimeoutTimerID - self.idleReadTimeoutTimer = self.eventLoop.assumeIsolated().scheduleTask(in: timeAmount) { - guard self.currentIdleReadTimeoutTimerID == timerID else { return } - let action = self.state.idleReadTimeoutTriggered() - self.run(action, context: context) - } - - case .resetIdleReadTimeoutTimer(let timeAmount): - if let oldTimer = self.idleReadTimeoutTimer { - oldTimer.cancel() - } - - self.currentIdleReadTimeoutTimerID &+= 1 - let timerID = self.currentIdleReadTimeoutTimerID - self.idleReadTimeoutTimer = self.eventLoop.assumeIsolated().scheduleTask(in: timeAmount) { - guard self.currentIdleReadTimeoutTimerID == timerID else { return } - let action = self.state.idleReadTimeoutTriggered() - self.run(action, context: context) - } - case .clearIdleReadTimeoutTimer: - if let oldTimer = self.idleReadTimeoutTimer { - self.idleReadTimeoutTimer = nil - self.currentIdleReadTimeoutTimerID &+= 1 - oldTimer.cancel() - } - case .none: - break - } - } - - private func runTimeoutAction(_ action: IdleWriteStateMachine.Action, context: ChannelHandlerContext) { - switch action { - case .startIdleWriteTimeoutTimer(let timeAmount): - assert(self.idleWriteTimeoutTimer == nil, "Expected there is no timeout timer so far.") - - let timerID = self.currentIdleWriteTimeoutTimerID - self.idleWriteTimeoutTimer = self.eventLoop.assumeIsolated().scheduleTask(in: timeAmount) { - guard self.currentIdleWriteTimeoutTimerID == timerID else { return } - let action = self.state.idleWriteTimeoutTriggered() - self.run(action, context: context) - } - case .resetIdleWriteTimeoutTimer(let timeAmount): - if let oldTimer = self.idleWriteTimeoutTimer { - oldTimer.cancel() - } - - self.currentIdleWriteTimeoutTimerID &+= 1 - let timerID = self.currentIdleWriteTimeoutTimerID - self.idleWriteTimeoutTimer = self.eventLoop.assumeIsolated().scheduleTask(in: timeAmount) { - guard self.currentIdleWriteTimeoutTimerID == timerID else { return } - let action = self.state.idleWriteTimeoutTriggered() - self.run(action, context: context) - } - case .clearIdleWriteTimeoutTimer: - if let oldTimer = self.idleWriteTimeoutTimer { - self.idleWriteTimeoutTimer = nil - self.currentIdleWriteTimeoutTimerID &+= 1 - oldTimer.cancel() - } - case .none: - break - } - } - - // MARK: Private HTTPRequestExecutor - - fileprivate func writeRequestBodyPart0( - _ data: IOData, - request: HTTPExecutableRequest, - promise: EventLoopPromise? - ) { - guard self.request === request, let context = self.channelContext else { - // Because the HTTPExecutableRequest may run in a different thread to our eventLoop, - // calls from the HTTPExecutableRequest to our ChannelHandler may arrive here after - // the request has been popped by the state machine or the ChannelHandler has been - // removed from the Channel pipeline. This is a normal threading issue, noone has - // screwed up. - promise?.fail(HTTPClientError.requestStreamCancelled) - return - } - - if let timeoutAction = self.idleWriteTimeoutStateMachine?.write() { - self.runTimeoutAction(timeoutAction, context: context) - } - - let action = self.state.requestStreamPartReceived(data, promise: promise) - self.run(action, context: context) - } - - fileprivate func finishRequestBodyStream0(_ request: HTTPExecutableRequest, promise: EventLoopPromise?) { - guard self.request === request, let context = self.channelContext else { - // See code comment in `writeRequestBodyPart0` - promise?.fail(HTTPClientError.requestStreamCancelled) - return - } - - let action = self.state.requestStreamFinished(promise: promise) - self.run(action, context: context) - } - - fileprivate func demandResponseBodyStream0(_ request: HTTPExecutableRequest) { - guard self.request === request, let context = self.channelContext else { - // See code comment in `writeRequestBodyPart0` - return - } - - self.logger.trace("Downstream requests more response body data") - - let action = self.state.demandMoreResponseBodyParts() - self.run(action, context: context) - } - - fileprivate func cancelRequest0(_ request: HTTPExecutableRequest) { - guard self.request === request, let context = self.channelContext else { - // See code comment in `writeRequestBodyPart0` - return - } - - self.logger.trace("Request was cancelled") - - if let timeoutAction = self.idleWriteTimeoutStateMachine?.cancelRequest() { - self.runTimeoutAction(timeoutAction, context: context) - } - - let action = self.state.requestCancelled(closeConnection: true) - self.run(action, context: context) - } -} - -@available(*, unavailable) -extension HTTP1ClientChannelHandler: Sendable {} - -extension HTTP1ClientChannelHandler { - var requestExecutor: RequestExecutor { - RequestExecutor(self) - } - - struct RequestExecutor: HTTPRequestExecutor, Sendable { - private let loopBound: NIOLoopBound - - init(_ handler: HTTP1ClientChannelHandler) { - self.loopBound = NIOLoopBound(handler, eventLoop: handler.eventLoop) - } - - func writeRequestBodyPart(_ data: IOData, request: HTTPExecutableRequest, promise: EventLoopPromise?) { - self.loopBound.execute { - $0.writeRequestBodyPart0(data, request: request, promise: promise) - } - } - - func finishRequestBodyStream(_ request: HTTPExecutableRequest, promise: EventLoopPromise?) { - self.loopBound.execute { - $0.finishRequestBodyStream0(request, promise: promise) - } - } - - func demandResponseBodyStream(_ request: HTTPExecutableRequest) { - self.loopBound.execute { - $0.demandResponseBodyStream0(request) - } - } - - func cancelRequest(_ request: HTTPExecutableRequest) { - self.loopBound.execute { - $0.cancelRequest0(request) - } - } - } -} - -struct IdleReadStateMachine { - enum Action { - case startIdleReadTimeoutTimer(TimeAmount) - case resetIdleReadTimeoutTimer(TimeAmount) - case clearIdleReadTimeoutTimer - case none - } - - enum State { - case waitingForRequestEnd - case waitingForMoreResponseData - case responseEndReceived - } - - private var state: State = .waitingForRequestEnd - private let timeAmount: TimeAmount - - init(timeAmount: TimeAmount) { - self.timeAmount = timeAmount - } - - mutating func requestEndSent() -> Action { - switch self.state { - case .waitingForRequestEnd: - self.state = .waitingForMoreResponseData - return .startIdleReadTimeoutTimer(self.timeAmount) - - case .waitingForMoreResponseData: - preconditionFailure("Invalid state. Waiting for response data must start after request head was sent") - - case .responseEndReceived: - // the response end was received, before we send the request head. Idle timeout timer - // must never be started. - return .none - } - } - - mutating func channelRead(_ part: HTTPClientResponsePart) -> Action { - switch self.state { - case .waitingForRequestEnd: - switch part { - case .head, .body: - return .none - case .end: - self.state = .responseEndReceived - return .none - } - - case .waitingForMoreResponseData: - switch part { - case .head, .body: - return .resetIdleReadTimeoutTimer(self.timeAmount) - case .end: - self.state = .responseEndReceived - return .none - } - - case .responseEndReceived: - preconditionFailure("How can we receive more data, if we already received the response end?") - } - } -} - -struct IdleWriteStateMachine { - enum Action { - case startIdleWriteTimeoutTimer(TimeAmount) - case resetIdleWriteTimeoutTimer(TimeAmount) - case clearIdleWriteTimeoutTimer - case none - } - - enum State { - case waitingForRequestEnd - case waitingForWritabilityEnabled - case requestEndSent - } - - private var state: State - private let timeAmount: TimeAmount - - init(timeAmount: TimeAmount, isWritabilityEnabled: Bool) { - self.timeAmount = timeAmount - if isWritabilityEnabled { - self.state = .waitingForRequestEnd - } else { - self.state = .waitingForWritabilityEnabled - } - } - - mutating func cancelRequest() -> Action { - switch self.state { - case .waitingForRequestEnd, .waitingForWritabilityEnabled: - self.state = .requestEndSent - return .clearIdleWriteTimeoutTimer - case .requestEndSent: - return .none - } - } - - mutating func write() -> Action { - switch self.state { - case .waitingForRequestEnd: - return .resetIdleWriteTimeoutTimer(self.timeAmount) - case .waitingForWritabilityEnabled: - return .none - case .requestEndSent: - preconditionFailure("If the request end has been sent, we can't write more data.") - } - } - - mutating func requestEndSent() -> Action { - switch self.state { - case .waitingForRequestEnd: - self.state = .requestEndSent - return .clearIdleWriteTimeoutTimer - case .waitingForWritabilityEnabled: - self.state = .requestEndSent - return .none - case .requestEndSent: - return .none - } - } - - mutating func channelWritabilityChanged(context: ChannelHandlerContext) -> Action { - if context.channel.isWritable { - switch self.state { - case .waitingForRequestEnd: - preconditionFailure("If waiting for more data, the channel was already writable.") - case .waitingForWritabilityEnabled: - self.state = .waitingForRequestEnd - return .startIdleWriteTimeoutTimer(self.timeAmount) - case .requestEndSent: - return .none - } - } else { - switch self.state { - case .waitingForRequestEnd: - self.state = .waitingForWritabilityEnabled - return .clearIdleWriteTimeoutTimer - case .waitingForWritabilityEnabled: - preconditionFailure( - "If the channel was writable before, then we should have been waiting for more data." - ) - case .requestEndSent: - return .none - } - } - } -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/HTTP1/HTTP1Connection.swift b/Sources/AsyncHTTPClient/ConnectionPool/HTTP1/HTTP1Connection.swift deleted file mode 100644 index 6f64e0407..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/HTTP1/HTTP1Connection.swift +++ /dev/null @@ -1,168 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOCore -import NIOHTTP1 -import NIOHTTPCompression - -protocol HTTP1ConnectionDelegate: Sendable { - func http1ConnectionReleased(_: HTTPConnectionPool.Connection.ID) - func http1ConnectionClosed(_: HTTPConnectionPool.Connection.ID) -} - -final class HTTP1Connection { - let channel: Channel - - /// the connection's delegate, that will be informed about connection close and connection release - /// (ready to run next request). - let delegate: HTTP1ConnectionDelegate - - enum State { - case initialized - case active - case closed - } - - private var state: State = .initialized - - let id: HTTPConnectionPool.Connection.ID - - init( - channel: Channel, - connectionID: HTTPConnectionPool.Connection.ID, - delegate: HTTP1ConnectionDelegate - ) { - self.channel = channel - self.id = connectionID - self.delegate = delegate - } - - deinit { - guard case .closed = self.state else { - preconditionFailure("Connection must be closed, before we can deinit it") - } - } - - static func start( - channel: Channel, - connectionID: HTTPConnectionPool.Connection.ID, - delegate: HTTP1ConnectionDelegate, - decompression: HTTPClient.Decompression, - logger: Logger - ) throws -> HTTP1Connection { - let connection = HTTP1Connection(channel: channel, connectionID: connectionID, delegate: delegate) - try connection.start(decompression: decompression, logger: logger) - return connection - } - - var sendableView: SendableView { - SendableView(self) - } - - struct SendableView: Sendable { - private let connection: NIOLoopBound - let channel: Channel - let id: HTTPConnectionPool.Connection.ID - private var eventLoop: EventLoop { self.connection.eventLoop } - - init(_ connection: HTTP1Connection) { - self.connection = NIOLoopBound(connection, eventLoop: connection.channel.eventLoop) - self.id = connection.id - self.channel = connection.channel - } - - func executeRequest(_ request: HTTPExecutableRequest) { - self.connection.execute { - $0.execute0(request: request) - } - } - - func shutdown() { - self.channel.triggerUserOutboundEvent(HTTPConnectionEvent.shutdownRequested, promise: nil) - } - - func close(promise: EventLoopPromise?) { - self.channel.close(mode: .all, promise: promise) - } - - func close() -> EventLoopFuture { - let promise = self.eventLoop.makePromise(of: Void.self) - self.close(promise: promise) - return promise.futureResult - } - } - - func taskCompleted() { - self.delegate.http1ConnectionReleased(self.id) - } - - private func execute0(request: HTTPExecutableRequest) { - guard self.channel.isActive else { - return request.fail(ChannelError.ioOnClosedChannel) - } - - self.channel.pipeline.syncOperations.write(NIOAny(request), promise: nil) - } - - private func start(decompression: HTTPClient.Decompression, logger: Logger) throws { - self.channel.eventLoop.assertInEventLoop() - - guard case .initialized = self.state else { - preconditionFailure("Connection must be initialized, to start it") - } - - self.state = .active - self.channel.closeFuture.assumeIsolated().whenComplete { _ in - self.state = .closed - self.delegate.http1ConnectionClosed(self.id) - } - - do { - let sync = self.channel.pipeline.syncOperations - - // We can not use `sync.addHTTPClientHandlers()`, as we want to explicitly set the - // `.informationalResponseStrategy` for the decoder. - let requestEncoder = HTTPRequestEncoder() - let responseDecoder = HTTPResponseDecoder( - leftOverBytesStrategy: .dropBytes, - informationalResponseStrategy: .forward - ) - try sync.addHandler(requestEncoder) - try sync.addHandler(ByteToMessageHandler(responseDecoder)) - - if case .enabled(let limit) = decompression { - let decompressHandler = NIOHTTPResponseDecompressor(limit: limit) - try sync.addHandler(decompressHandler) - } - - let channelHandler = HTTP1ClientChannelHandler( - eventLoop: channel.eventLoop, - backgroundLogger: logger, - connectionIdLoggerMetadata: "\(self.id)" - ) - channelHandler.onConnectionIdle = { - self.taskCompleted() - } - - try sync.addHandler(channelHandler) - } catch { - self.channel.close(mode: .all, promise: nil) - throw error - } - } -} - -@available(*, unavailable) -extension HTTP1Connection: Sendable {} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/HTTP1/HTTP1ConnectionStateMachine.swift b/Sources/AsyncHTTPClient/ConnectionPool/HTTP1/HTTP1ConnectionStateMachine.swift deleted file mode 100644 index 2cde1df3f..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/HTTP1/HTTP1ConnectionStateMachine.swift +++ /dev/null @@ -1,521 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOHTTP1 - -struct HTTP1ConnectionStateMachine { - fileprivate enum State { - case initialized - case idle - case inRequest(HTTPRequestStateMachine, close: Bool) - case closing - case closed - - case modifying - } - - enum Action { - /// A action to execute, when we consider a request "done". - enum FinalSuccessfulStreamAction { - /// Close the connection - case close - /// If the server has replied, with a status of 200...300 before all data was sent, a request is considered succeeded, - /// as soon as we wrote the request end onto the wire. - /// - /// The promise is an optional write promise. - /// - /// `shouldClose` records whether we have attached a Connection: close header to this request, and so the connection should - /// be terminated - case sendRequestEnd(EventLoopPromise?, shouldClose: Bool) - /// Inform an observer that the connection has become idle - case informConnectionIsIdle - } - - /// A action to execute, when we consider a request "done". - enum FinalFailedStreamAction { - /// Close the connection - /// - /// The promise is an optional write promise. - case close(EventLoopPromise?) - /// Inform an observer that the connection has become idle - case informConnectionIsIdle - /// Fail the write promise - case failWritePromise(EventLoopPromise?) - /// Do nothing. - case none - } - - case sendRequestHead(HTTPRequestHead, sendEnd: Bool) - case notifyRequestHeadSendSuccessfully( - resumeRequestBodyStream: Bool, - startIdleTimer: Bool - ) - case sendBodyPart(IOData, EventLoopPromise?) - case sendRequestEnd(EventLoopPromise?) - case failSendBodyPart(Error, EventLoopPromise?) - case failSendStreamFinished(Error, EventLoopPromise?) - - case pauseRequestBodyStream - case resumeRequestBodyStream - - case forwardResponseHead(HTTPResponseHead, pauseRequestBodyStream: Bool) - case forwardResponseBodyParts(CircularBuffer) - - case failRequest(Error, FinalFailedStreamAction) - case succeedRequest(FinalSuccessfulStreamAction, CircularBuffer) - - case read - case close - case wait - - case fireChannelActive - case fireChannelInactive - case fireChannelError(Error, closeConnection: Bool) - } - - private var state: State - private var isChannelWritable: Bool = true - - init() { - self.state = .initialized - } - - mutating func channelActive(isWritable: Bool) -> Action { - switch self.state { - case .initialized: - self.isChannelWritable = isWritable - self.state = .idle - return .fireChannelActive - - case .idle, .inRequest, .closing, .closed: - // Since NIO triggers promise before pipeline, the handler might have been added to the - // pipeline, before the channelActive callback was triggered. For this reason, we might - // get the channelActive call twice - return .wait - - case .modifying: - fatalError("Invalid state: \(self.state)") - } - } - - mutating func channelInactive() -> Action { - switch self.state { - case .initialized: - fatalError("A channel that isn't active, must not become inactive") - - case .inRequest(var requestStateMachine, close: _): - return self.avoidingStateMachineCoW { state -> Action in - let action = requestStateMachine.channelInactive() - state = .closed - return state.modify(with: action) - } - - case .idle, .closing: - self.state = .closed - return .fireChannelInactive - - case .closed: - return .wait - - case .modifying: - fatalError("Invalid state: \(self.state)") - } - } - - mutating func errorHappened(_ error: Error) -> Action { - switch self.state { - case .initialized: - self.state = .closed - return .fireChannelError(error, closeConnection: false) - - case .inRequest(var requestStateMachine, let close): - return self.avoidingStateMachineCoW { state -> Action in - let action = requestStateMachine.errorHappened(error) - state = .inRequest(requestStateMachine, close: close) - return state.modify(with: action) - } - - case .idle: - self.state = .closing - return .fireChannelError(error, closeConnection: true) - - case .closing, .closed: - return .fireChannelError(error, closeConnection: false) - - case .modifying: - fatalError("Invalid state: \(self.state)") - } - } - - mutating func writabilityChanged(writable: Bool) -> Action { - self.isChannelWritable = writable - - switch self.state { - case .initialized, .idle, .closing, .closed: - return .wait - case .inRequest(var requestStateMachine, let close): - return self.avoidingStateMachineCoW { state -> Action in - let action = requestStateMachine.writabilityChanged(writable: writable) - state = .inRequest(requestStateMachine, close: close) - return state.modify(with: action) - } - - case .modifying: - fatalError("Invalid state: \(self.state)") - } - } - - mutating func runNewRequest( - head: HTTPRequestHead, - metadata: RequestFramingMetadata - ) -> Action { - switch self.state { - case .initialized, .inRequest: - // These states are unreachable as the connection pool state machine has put the - // connection into these states. In other words the connection pool state machine must - // be aware about these states before the connection itself. For this reason the - // connection pool state machine must not send a new request to the connection, if the - // connection is `.initialized`, `.closing` or `.inRequest` - fatalError("Invalid state: \(self.state)") - - case .closing, .closed: - // The remote may have closed the connection and the connection pool state machine - // was not updated yet because of a race condition. New request vs. marking connection - // as closed. - // - // TODO: AHC should support a fast rescheduling mechanism here. - return .failRequest(HTTPClientError.remoteConnectionClosed, .none) - - case .idle: - var requestStateMachine = HTTPRequestStateMachine(isChannelWritable: self.isChannelWritable) - let action = requestStateMachine.startRequest(head: head, metadata: metadata) - - // by default we assume a persistent connection. however in `requestVerified`, we read the - // "connection" header. - self.state = .inRequest(requestStateMachine, close: metadata.connectionClose) - return self.state.modify(with: action) - - case .modifying: - fatalError("Invalid state: \(self.state)") - } - } - - mutating func requestStreamPartReceived(_ part: IOData, promise: EventLoopPromise?) -> Action { - guard case .inRequest(var requestStateMachine, let close) = self.state else { - fatalError("Invalid state: \(self.state)") - } - - return self.avoidingStateMachineCoW { state -> Action in - let action = requestStateMachine.requestStreamPartReceived(part, promise: promise) - state = .inRequest(requestStateMachine, close: close) - return state.modify(with: action) - } - } - - mutating func requestStreamFinished(promise: EventLoopPromise?) -> Action { - guard case .inRequest(var requestStateMachine, let close) = self.state else { - fatalError("Invalid state: \(self.state)") - } - - return self.avoidingStateMachineCoW { state -> Action in - let action = requestStateMachine.requestStreamFinished(promise: promise) - state = .inRequest(requestStateMachine, close: close) - return state.modify(with: action) - } - } - - mutating func requestCancelled(closeConnection: Bool) -> Action { - switch self.state { - case .initialized: - fatalError( - "This event must only happen, if the connection is leased. During startup this is impossible. Invalid state: \(self.state)" - ) - - case .idle: - if closeConnection { - self.state = .closing - return .close - } else { - return .wait - } - - case .inRequest(var requestStateMachine, let close): - return self.avoidingStateMachineCoW { state -> Action in - let action = requestStateMachine.requestCancelled() - state = .inRequest(requestStateMachine, close: close || closeConnection) - return state.modify(with: action) - } - - case .closing, .closed: - return .wait - - case .modifying: - fatalError("Invalid state: \(self.state)") - } - } - - // MARK: - Response - - mutating func read() -> Action { - switch self.state { - case .initialized: - fatalError("Why should we read something, if we are not connected yet") - case .idle: - return .read - case .inRequest(var requestStateMachine, let close): - return self.avoidingStateMachineCoW { state -> Action in - let action = requestStateMachine.read() - state = .inRequest(requestStateMachine, close: close) - return state.modify(with: action) - } - - case .closing, .closed: - // there might be a race in us closing the connection and receiving another read event - return .read - - case .modifying: - fatalError("Invalid state: \(self.state)") - } - } - - mutating func channelRead(_ part: HTTPClientResponsePart) -> Action { - switch self.state { - case .initialized, .idle: - fatalError("Invalid state: \(self.state)") - - case .inRequest(var requestStateMachine, var close): - return self.avoidingStateMachineCoW { state -> Action in - let action = requestStateMachine.channelRead(part) - - if case .head(let head) = part, close == false { - // since the HTTPClient does not support protocol switching, we must close any - // connection that has received a status `.switchingProtocols` - close = !head.isKeepAlive || head.status == .switchingProtocols - } - state = .inRequest(requestStateMachine, close: close) - return state.modify(with: action) - } - - case .closing, .closed: - return .wait - - case .modifying: - fatalError("Invalid state: \(self.state)") - } - } - - mutating func channelReadComplete() -> Action { - switch self.state { - case .initialized, .idle, .closing, .closed: - return .wait - - case .inRequest(var requestStateMachine, let close): - return self.avoidingStateMachineCoW { state -> Action in - let action = requestStateMachine.channelReadComplete() - state = .inRequest(requestStateMachine, close: close) - return state.modify(with: action) - } - - case .modifying: - fatalError("Invalid state: \(self.state)") - } - } - - mutating func demandMoreResponseBodyParts() -> Action { - guard case .inRequest(var requestStateMachine, let close) = self.state else { - fatalError("Invalid state: \(self.state)") - } - - return self.avoidingStateMachineCoW { state -> Action in - let action = requestStateMachine.demandMoreResponseBodyParts() - state = .inRequest(requestStateMachine, close: close) - return state.modify(with: action) - } - } - - mutating func idleReadTimeoutTriggered() -> Action { - guard case .inRequest(var requestStateMachine, let close) = self.state else { - fatalError("Invalid state: \(self.state)") - } - - return self.avoidingStateMachineCoW { state -> Action in - let action = requestStateMachine.idleReadTimeoutTriggered() - state = .inRequest(requestStateMachine, close: close) - return state.modify(with: action) - } - } - - mutating func idleWriteTimeoutTriggered() -> Action { - guard case .inRequest(var requestStateMachine, let close) = self.state else { - return .wait - } - - return self.avoidingStateMachineCoW { state -> Action in - let action = requestStateMachine.idleWriteTimeoutTriggered() - state = .inRequest(requestStateMachine, close: close) - return state.modify(with: action) - } - } - - mutating func headSent() -> Action { - guard case .inRequest(var requestStateMachine, let close) = self.state else { - return .wait - } - return self.avoidingStateMachineCoW { state in - let action = requestStateMachine.headSent() - state = .inRequest(requestStateMachine, close: close) - return state.modify(with: action) - } - } -} - -extension HTTP1ConnectionStateMachine { - /// So, uh...this function needs some explaining. - /// - /// While the state machine logic above is great, there is a downside to having all of the state machine data in - /// associated data on enumerations: any modification of that data will trigger copy on write for heap-allocated - /// data. That means that for _every operation on the state machine_ we will CoW our underlying state, which is - /// not good. - /// - /// The way we can avoid this is by using this helper function. It will temporarily set state to a value with no - /// associated data, before attempting the body of the function. It will also verify that the state machine never - /// remains in this bad state. - /// - /// A key note here is that all callers must ensure that they return to a good state before they exit. - /// - /// Sadly, because it's generic and has a closure, we need to force it to be inlined at all call sites, which is - /// not ideal. - @inline(__always) - private mutating func avoidingStateMachineCoW(_ body: (inout State) -> ReturnType) -> ReturnType { - self.state = .modifying - defer { - assert(!self.isModifying) - } - - return body(&self.state) - } - - private var isModifying: Bool { - if case .modifying = self.state { - return true - } else { - return false - } - } -} - -extension HTTP1ConnectionStateMachine.State { - fileprivate mutating func modify(with action: HTTPRequestStateMachine.Action) -> HTTP1ConnectionStateMachine.Action - { - switch action { - case .sendRequestHead(let head, let sendEnd): - return .sendRequestHead(head, sendEnd: sendEnd) - case .notifyRequestHeadSendSuccessfully(let resumeRequestBodyStream, let startIdleTimer): - return .notifyRequestHeadSendSuccessfully( - resumeRequestBodyStream: resumeRequestBodyStream, - startIdleTimer: startIdleTimer - ) - case .pauseRequestBodyStream: - return .pauseRequestBodyStream - case .resumeRequestBodyStream: - return .resumeRequestBodyStream - case .sendBodyPart(let part, let writePromise): - return .sendBodyPart(part, writePromise) - case .sendRequestEnd(let writePromise): - return .sendRequestEnd(writePromise) - case .forwardResponseHead(let head, let pauseRequestBodyStream): - return .forwardResponseHead(head, pauseRequestBodyStream: pauseRequestBodyStream) - case .forwardResponseBodyParts(let parts): - return .forwardResponseBodyParts(parts) - case .succeedRequest(let finalAction, let finalParts): - guard case .inRequest(_, close: let close) = self else { - fatalError("Invalid state: \(self)") - } - - let newFinalAction: HTTP1ConnectionStateMachine.Action.FinalSuccessfulStreamAction - switch finalAction { - case .close: - self = .closing - newFinalAction = .close - case .sendRequestEnd(let writePromise): - self = .idle - newFinalAction = .sendRequestEnd(writePromise, shouldClose: close) - case .none: - self = .idle - newFinalAction = close ? .close : .informConnectionIsIdle - } - return .succeedRequest(newFinalAction, finalParts) - - case .failRequest(let error, let finalAction): - switch self { - case .initialized: - fatalError("Invalid state: \(self)") - case .idle: - fatalError("How can we fail a task, if we are idle") - case .inRequest(_, let close): - if case .close(let promise) = finalAction { - self = .closing - return .failRequest(error, .close(promise)) - } else if close { - self = .closing - return .failRequest(error, .close(nil)) - } else { - self = .idle - return .failRequest(error, .informConnectionIsIdle) - } - - case .closing: - return .failRequest(error, .none) - case .closed: - // this state can be reached, if the connection was unexpectedly closed by remote - return .failRequest(error, .none) - - case .modifying: - fatalError("Invalid state: \(self)") - } - - case .read: - return .read - - case .wait: - return .wait - - case .failSendBodyPart(let error, let writePromise): - return .failSendBodyPart(error, writePromise) - - case .failSendStreamFinished(let error, let writePromise): - return .failSendStreamFinished(error, writePromise) - } - } -} - -extension HTTP1ConnectionStateMachine: CustomStringConvertible { - var description: String { - switch self.state { - case .initialized: - return ".initialized" - case .idle: - return ".idle" - case .inRequest(let request, let close): - return ".inRequest(\(request), closeAfterRequest: \(close))" - case .closing: - return ".closing" - case .closed: - return ".closed" - case .modifying: - fatalError("Invalid state: \(self.state)") - } - } -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/HTTP2/HTTP2ClientRequestHandler.swift b/Sources/AsyncHTTPClient/ConnectionPool/HTTP2/HTTP2ClientRequestHandler.swift deleted file mode 100644 index 7c0197cdf..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/HTTP2/HTTP2ClientRequestHandler.swift +++ /dev/null @@ -1,476 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOCore -import NIOHTTP1 -import NIOHTTP2 - -final class HTTP2ClientRequestHandler: ChannelDuplexHandler { - typealias OutboundIn = HTTPExecutableRequest - typealias OutboundOut = HTTPClientRequestPart - typealias InboundIn = HTTPClientResponsePart - - private let eventLoop: EventLoop - - private var state: HTTPRequestStateMachine = .init(isChannelWritable: false) { - willSet { - self.eventLoop.assertInEventLoop() - } - } - - /// while we are in a channel pipeline, this context can be used. - private var channelContext: ChannelHandlerContext? - - private var request: HTTPExecutableRequest? { - didSet { - if let newRequest = self.request { - if let idleReadTimeout = newRequest.requestOptions.idleReadTimeout { - self.idleReadTimeoutStateMachine = .init(timeAmount: idleReadTimeout) - } - if let idleWriteTimeout = newRequest.requestOptions.idleWriteTimeout { - self.idleWriteTimeoutStateMachine = .init( - timeAmount: idleWriteTimeout, - isWritabilityEnabled: self.channelContext?.channel.isWritable ?? false - ) - } - } else { - self.idleReadTimeoutStateMachine = nil - } - } - } - - private var idleReadTimeoutStateMachine: IdleReadStateMachine? - private var idleReadTimeoutTimer: Scheduled? - - private var idleWriteTimeoutStateMachine: IdleWriteStateMachine? - private var idleWriteTimeoutTimer: Scheduled? - - /// Cancelling a task in NIO does *not* guarantee that the task will not execute under certain race conditions. - /// We therefore give each timer an ID and increase the ID every time we reset or cancel it. - /// We check in the task if the timer ID has changed in the meantime and do not execute any action if has changed. - private var currentIdleReadTimeoutTimerID: Int = 0 - private var currentIdleWriteTimeoutTimerID: Int = 0 - - init(eventLoop: EventLoop) { - self.eventLoop = eventLoop - } - - func handlerAdded(context: ChannelHandlerContext) { - assert( - context.eventLoop === self.eventLoop, - "The handler must be added to a channel that runs on the eventLoop it was initialized with." - ) - self.channelContext = context - - let isWritable = context.channel.isActive && context.channel.isWritable - let action = self.state.writabilityChanged(writable: isWritable) - self.run(action, context: context) - } - - func handlerRemoved(context: ChannelHandlerContext) { - self.channelContext = nil - } - - // MARK: Channel Inbound Handler - - func channelActive(context: ChannelHandlerContext) { - let action = self.state.writabilityChanged(writable: context.channel.isWritable) - self.run(action, context: context) - } - - func channelInactive(context: ChannelHandlerContext) { - let action = self.state.channelInactive() - self.run(action, context: context) - } - - func channelWritabilityChanged(context: ChannelHandlerContext) { - if let timeoutAction = self.idleWriteTimeoutStateMachine?.channelWritabilityChanged(context: context) { - self.runTimeoutAction(timeoutAction, context: context) - } - - let action = self.state.writabilityChanged(writable: context.channel.isWritable) - self.run(action, context: context) - } - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - let httpPart = self.unwrapInboundIn(data) - - if let timeoutAction = self.idleReadTimeoutStateMachine?.channelRead(httpPart) { - self.runTimeoutAction(timeoutAction, context: context) - } - - let action = self.state.channelRead(httpPart) - self.run(action, context: context) - } - - func channelReadComplete(context: ChannelHandlerContext) { - let action = self.state.channelReadComplete() - self.run(action, context: context) - } - - func errorCaught(context: ChannelHandlerContext, error: Error) { - let action = self.state.errorHappened(error) - self.run(action, context: context) - } - - // MARK: Channel Outbound Handler - - func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise?) { - let request = self.unwrapOutboundIn(data) - // The `HTTPRequestStateMachine` ensures that a `HTTP2ClientRequestHandler` only handles - // a single request. - self.request = request - - if let timeoutAction = self.idleWriteTimeoutStateMachine?.write() { - self.runTimeoutAction(timeoutAction, context: context) - } - - request.willExecuteRequest(self.requestExecutor) - - let action = self.state.startRequest( - head: request.requestHead, - metadata: request.requestFramingMetadata - ) - self.run(action, context: context) - } - - func read(context: ChannelHandlerContext) { - let action = self.state.read() - self.run(action, context: context) - } - - func triggerUserOutboundEvent(context: ChannelHandlerContext, event: Any, promise: EventLoopPromise?) { - switch event { - case HTTPConnectionEvent.shutdownRequested: - let action = self.state.requestCancelled() - self.run(action, context: context) - default: - context.fireUserInboundEventTriggered(event) - } - } - - // MARK: - Private Methods - - - // MARK: Run Actions - - private func run(_ action: HTTPRequestStateMachine.Action, context: ChannelHandlerContext) { - switch action { - case .sendRequestHead(let head, let sendEnd): - self.sendRequestHead(head, sendEnd: sendEnd, context: context) - case .notifyRequestHeadSendSuccessfully(let resumeRequestBodyStream, let startIdleTimer): - // We can force unwrap the request here, as we have just validated in the state machine, - // that the request is neither failed nor finished yet - self.request!.requestHeadSent() - if resumeRequestBodyStream, let request = self.request { - // The above request head send notification might lead the request to mark itself as - // cancelled, which in turn might pop the request of the handler. For this reason we - // must check if the request is still present here. - request.resumeRequestBodyStream() - } - if startIdleTimer { - if let readTimeoutAction = self.idleReadTimeoutStateMachine?.requestEndSent() { - self.runTimeoutAction(readTimeoutAction, context: context) - } - - if let writeTimeoutAction = self.idleWriteTimeoutStateMachine?.requestEndSent() { - self.runTimeoutAction(writeTimeoutAction, context: context) - } - } - case .pauseRequestBodyStream: - // We can force unwrap the request here, as we have just validated in the state machine, - // that the request is neither failed nor finished yet - self.request!.pauseRequestBodyStream() - - case .sendBodyPart(let data, let writePromise): - context.writeAndFlush(self.wrapOutboundOut(.body(data)), promise: writePromise) - - case .sendRequestEnd(let writePromise): - context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: writePromise) - - if let readTimeoutAction = self.idleReadTimeoutStateMachine?.requestEndSent() { - self.runTimeoutAction(readTimeoutAction, context: context) - } - - if let writeTimeoutAction = self.idleWriteTimeoutStateMachine?.requestEndSent() { - self.runTimeoutAction(writeTimeoutAction, context: context) - } - - case .read: - context.read() - - case .wait: - break - - case .resumeRequestBodyStream: - // We can force unwrap the request here, as we have just validated in the state machine, - // that the request is neither failed nor finished yet - self.request!.resumeRequestBodyStream() - - case .forwardResponseHead(let head, let pauseRequestBodyStream): - // We can force unwrap the request here, as we have just validated in the state machine, - // that the request is neither failed nor finished yet - self.request!.receiveResponseHead(head) - if pauseRequestBodyStream, let request = self.request { - // The above response head forward might lead the request to mark itself as - // cancelled, which in turn might pop the request of the handler. For this reason we - // must check if the request is still present here. - request.pauseRequestBodyStream() - } - - case .forwardResponseBodyParts(let parts): - // We can force unwrap the request here, as we have just validated in the state machine, - // that the request is neither failed nor finished yet - self.request!.receiveResponseBodyParts(parts) - - case .failRequest(let error, let finalAction): - // We can force unwrap the request here, as we have just validated in the state machine, - // that the request object is still present. - self.request!.fail(error) - self.request = nil - self.runTimeoutAction(.clearIdleReadTimeoutTimer, context: context) - self.runTimeoutAction(.clearIdleWriteTimeoutTimer, context: context) - // No matter the error reason, we must always make sure the h2 stream is closed. Only - // once the h2 stream is closed, it is released from the h2 multiplexer. The - // HTTPRequestStateMachine may signal finalAction: .none in the error case (as this is - // the right result for HTTP/1). In the h2 case we MUST always close. - self.runFailedFinalAction(finalAction, context: context, error: error) - - case .succeedRequest(let finalAction, let finalParts): - // We can force unwrap the request here, as we have just validated in the state machine, - // that the request object is still present. - self.request!.succeedRequest(finalParts) - self.request = nil - self.runTimeoutAction(.clearIdleReadTimeoutTimer, context: context) - self.runTimeoutAction(.clearIdleWriteTimeoutTimer, context: context) - self.runSuccessfulFinalAction(finalAction, context: context) - - case .failSendBodyPart(let error, let writePromise), .failSendStreamFinished(let error, let writePromise): - writePromise?.fail(error) - } - } - - private func sendRequestHead(_ head: HTTPRequestHead, sendEnd: Bool, context: ChannelHandlerContext) { - if sendEnd { - context.write(self.wrapOutboundOut(.head(head)), promise: nil) - context.write(self.wrapOutboundOut(.end(nil)), promise: nil) - context.flush() - } else { - context.writeAndFlush(self.wrapOutboundOut(.head(head)), promise: nil) - } - self.run(self.state.headSent(), context: context) - } - - private func runSuccessfulFinalAction( - _ action: HTTPRequestStateMachine.Action.FinalSuccessfulRequestAction, - context: ChannelHandlerContext - ) { - switch action { - case .close, .none: - // The actions returned here come from an `HTTPRequestStateMachine` that assumes http/1.1 - // semantics. For this reason we can ignore the close here, since an h2 stream is closed - // after every request anyway. - break - - case .sendRequestEnd(let writePromise): - context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: writePromise) - } - } - - private func runFailedFinalAction( - _ action: HTTPRequestStateMachine.Action.FinalFailedRequestAction, - context: ChannelHandlerContext, - error: Error - ) { - // We must close the http2 stream after the request has finished. Since the request failed, - // we have no idea what the h2 streams state was. To be on the save side, we explicitly close - // the h2 stream. This will break a reference cycle in HTTP2Connection. - context.close(promise: nil) - - switch action { - case .close(let writePromise): - writePromise?.fail(error) - - case .none: - break - } - } - - private func runTimeoutAction(_ action: IdleReadStateMachine.Action, context: ChannelHandlerContext) { - switch action { - case .startIdleReadTimeoutTimer(let timeAmount): - assert(self.idleReadTimeoutTimer == nil, "Expected there is no timeout timer so far.") - - let timerID = self.currentIdleReadTimeoutTimerID - self.idleReadTimeoutTimer = self.eventLoop.assumeIsolated().scheduleTask(in: timeAmount) { - guard self.currentIdleReadTimeoutTimerID == timerID else { return } - let action = self.state.idleReadTimeoutTriggered() - self.run(action, context: context) - } - - case .resetIdleReadTimeoutTimer(let timeAmount): - if let oldTimer = self.idleReadTimeoutTimer { - oldTimer.cancel() - } - - self.currentIdleReadTimeoutTimerID &+= 1 - let timerID = self.currentIdleReadTimeoutTimerID - self.idleReadTimeoutTimer = self.eventLoop.assumeIsolated().scheduleTask(in: timeAmount) { - guard self.currentIdleReadTimeoutTimerID == timerID else { return } - let action = self.state.idleReadTimeoutTriggered() - self.run(action, context: context) - } - case .clearIdleReadTimeoutTimer: - if let oldTimer = self.idleReadTimeoutTimer { - self.idleReadTimeoutTimer = nil - self.currentIdleReadTimeoutTimerID &+= 1 - oldTimer.cancel() - } - - case .none: - break - } - } - - private func runTimeoutAction(_ action: IdleWriteStateMachine.Action, context: ChannelHandlerContext) { - switch action { - case .startIdleWriteTimeoutTimer(let timeAmount): - assert(self.idleWriteTimeoutTimer == nil, "Expected there is no timeout timer so far.") - - let timerID = self.currentIdleWriteTimeoutTimerID - self.idleWriteTimeoutTimer = self.eventLoop.assumeIsolated().scheduleTask(in: timeAmount) { - guard self.currentIdleWriteTimeoutTimerID == timerID else { return } - let action = self.state.idleWriteTimeoutTriggered() - self.run(action, context: context) - } - case .resetIdleWriteTimeoutTimer(let timeAmount): - if let oldTimer = self.idleWriteTimeoutTimer { - oldTimer.cancel() - } - - self.currentIdleWriteTimeoutTimerID &+= 1 - let timerID = self.currentIdleWriteTimeoutTimerID - self.idleWriteTimeoutTimer = self.eventLoop.assumeIsolated().scheduleTask(in: timeAmount) { - guard self.currentIdleWriteTimeoutTimerID == timerID else { return } - let action = self.state.idleWriteTimeoutTriggered() - self.run(action, context: context) - } - case .clearIdleWriteTimeoutTimer: - if let oldTimer = self.idleWriteTimeoutTimer { - self.idleWriteTimeoutTimer = nil - self.currentIdleWriteTimeoutTimerID &+= 1 - oldTimer.cancel() - } - case .none: - break - } - } - - // MARK: Private HTTPRequestExecutor - - private func writeRequestBodyPart0(_ data: IOData, request: HTTPExecutableRequest, promise: EventLoopPromise?) - { - guard self.request === request, let context = self.channelContext else { - // Because the HTTPExecutableRequest may run in a different thread to our eventLoop, - // calls from the HTTPExecutableRequest to our ChannelHandler may arrive here after - // the request has been popped by the state machine or the ChannelHandler has been - // removed from the Channel pipeline. This is a normal threading issue, noone has - // screwed up. - promise?.fail(HTTPClientError.requestStreamCancelled) - return - } - - if let timeoutAction = self.idleWriteTimeoutStateMachine?.write() { - self.runTimeoutAction(timeoutAction, context: context) - } - - let action = self.state.requestStreamPartReceived(data, promise: promise) - self.run(action, context: context) - } - - private func finishRequestBodyStream0(_ request: HTTPExecutableRequest, promise: EventLoopPromise?) { - guard self.request === request, let context = self.channelContext else { - // See code comment in `writeRequestBodyPart0` - return - } - - let action = self.state.requestStreamFinished(promise: promise) - self.run(action, context: context) - } - - private func demandResponseBodyStream0(_ request: HTTPExecutableRequest) { - guard self.request === request, let context = self.channelContext else { - // See code comment in `writeRequestBodyPart0` - return - } - - let action = self.state.demandMoreResponseBodyParts() - self.run(action, context: context) - } - - private func cancelRequest0(_ request: HTTPExecutableRequest) { - guard self.request === request, let context = self.channelContext else { - // See code comment in `writeRequestBodyPart0` - return - } - - if let timeoutAction = self.idleWriteTimeoutStateMachine?.cancelRequest() { - self.runTimeoutAction(timeoutAction, context: context) - } - - let action = self.state.requestCancelled() - self.run(action, context: context) - } -} - -@available(*, unavailable) -extension HTTP2ClientRequestHandler: Sendable {} - -extension HTTP2ClientRequestHandler { - var requestExecutor: RequestExecutor { - RequestExecutor(self) - } - - struct RequestExecutor: HTTPRequestExecutor, Sendable { - private let loopBound: NIOLoopBound - - init(_ handler: HTTP2ClientRequestHandler) { - self.loopBound = NIOLoopBound(handler, eventLoop: handler.eventLoop) - } - - func writeRequestBodyPart(_ data: IOData, request: HTTPExecutableRequest, promise: EventLoopPromise?) { - self.loopBound.execute { - $0.writeRequestBodyPart0(data, request: request, promise: promise) - } - } - - func finishRequestBodyStream(_ request: HTTPExecutableRequest, promise: EventLoopPromise?) { - self.loopBound.execute { - $0.finishRequestBodyStream0(request, promise: promise) - } - } - - func demandResponseBodyStream(_ request: HTTPExecutableRequest) { - self.loopBound.execute { - $0.demandResponseBodyStream0(request) - } - } - - func cancelRequest(_ request: HTTPExecutableRequest) { - self.loopBound.execute { - $0.cancelRequest0(request) - } - } - } -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/HTTP2/HTTP2Connection.swift b/Sources/AsyncHTTPClient/ConnectionPool/HTTP2/HTTP2Connection.swift deleted file mode 100644 index 1c24554e2..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/HTTP2/HTTP2Connection.swift +++ /dev/null @@ -1,401 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOCore -import NIOHTTP2 -import NIOHTTPCompression - -protocol HTTP2ConnectionDelegate: Sendable { - func http2Connection(_: HTTPConnectionPool.Connection.ID, newMaxStreamSetting: Int) - func http2ConnectionStreamClosed(_: HTTPConnectionPool.Connection.ID, availableStreams: Int) - func http2ConnectionGoAwayReceived(_: HTTPConnectionPool.Connection.ID) - func http2ConnectionClosed(_: HTTPConnectionPool.Connection.ID) -} - -struct HTTP2PushNotSupportedError: Error {} - -struct HTTP2ReceivedGoAwayBeforeSettingsError: Error {} - -final class HTTP2Connection { - internal static let defaultSettings = nioDefaultSettings + [HTTP2Setting(parameter: .enablePush, value: 0)] - - let channel: Channel - let multiplexer: HTTP2StreamMultiplexer - let logger: Logger - - /// A method with access to the stream channel that is called when creating the stream. - let streamChannelDebugInitializer: (@Sendable (Channel) -> EventLoopFuture)? - - /// the connection pool that created the connection - let delegate: HTTP2ConnectionDelegate - - enum State { - case initialized - case starting(EventLoopPromise) - case active(maxStreams: Int) - case closing - case closed - } - - /// A structure to store a http/2 stream channel in a set. - private struct ChannelBox: Hashable { - struct ID: Hashable { - private let id: ObjectIdentifier - - init(_ channel: Channel) { - self.id = ObjectIdentifier(channel) - } - } - - let channel: Channel - - var id: ID { - ID(self.channel) - } - - init(_ channel: Channel) { - self.channel = channel - } - - static func == (lhs: Self, rhs: Self) -> Bool { - lhs.id == rhs.id - } - - func hash(into hasher: inout Hasher) { - hasher.combine(self.id) - } - } - - private var state: State - - /// We use this channel set to remember, which open streams we need to inform that - /// we want to close the connection. The channels shall than cancel their currently running - /// request. This property must only be accessed from the connections `EventLoop`. - private var openStreams = Set() - let id: HTTPConnectionPool.Connection.ID - let decompression: HTTPClient.Decompression - let maximumConnectionUses: Int? - - var closeFuture: EventLoopFuture { - self.channel.closeFuture - } - - init( - channel: Channel, - connectionID: HTTPConnectionPool.Connection.ID, - decompression: HTTPClient.Decompression, - maximumConnectionUses: Int?, - delegate: HTTP2ConnectionDelegate, - logger: Logger, - streamChannelDebugInitializer: (@Sendable (Channel) -> EventLoopFuture)? = nil - ) { - self.channel = channel - self.id = connectionID - self.decompression = decompression - self.maximumConnectionUses = maximumConnectionUses - self.logger = logger - self.multiplexer = HTTP2StreamMultiplexer( - mode: .client, - channel: channel, - targetWindowSize: 8 * 1024 * 1024, // 8mb - outboundBufferSizeHighWatermark: 8196, - outboundBufferSizeLowWatermark: 4092, - inboundStreamInitializer: { channel -> EventLoopFuture in - channel.eventLoop.makeFailedFuture(HTTP2PushNotSupportedError()) - } - ) - self.delegate = delegate - self.state = .initialized - self.streamChannelDebugInitializer = streamChannelDebugInitializer - } - - deinit { - guard case .closed = self.state else { - preconditionFailure("Connection must be closed, before we can deinit it. Current state: \(self.state)") - } - } - - static func start( - channel: Channel, - connectionID: HTTPConnectionPool.Connection.ID, - delegate: HTTP2ConnectionDelegate, - decompression: HTTPClient.Decompression, - maximumConnectionUses: Int?, - logger: Logger, - streamChannelDebugInitializer: (@Sendable (Channel) -> EventLoopFuture)? = nil - ) -> EventLoopFuture<(HTTP2Connection, Int)>.Isolated { - let connection = HTTP2Connection( - channel: channel, - connectionID: connectionID, - decompression: decompression, - maximumConnectionUses: maximumConnectionUses, - delegate: delegate, - logger: logger, - streamChannelDebugInitializer: streamChannelDebugInitializer - ) - - return connection._start0().assumeIsolated().map { maxStreams in - (connection, maxStreams) - } - } - - var sendableView: SendableView { - SendableView(self) - } - - struct SendableView: Sendable { - private let connection: NIOLoopBound - let id: HTTPConnectionPool.Connection.ID - let channel: Channel - - var eventLoop: EventLoop { - self.connection.eventLoop - } - - var closeFuture: EventLoopFuture { - self.channel.closeFuture - } - - func __forTesting_getStreamChannels() -> [Channel] { - self.connection.value.__forTesting_getStreamChannels() - } - - init(_ connection: HTTP2Connection) { - self.connection = NIOLoopBound(connection, eventLoop: connection.channel.eventLoop) - self.id = connection.id - self.channel = connection.channel - } - - func executeRequest(_ request: HTTPExecutableRequest) { - self.connection.execute { - $0.executeRequest0(request) - } - } - - func shutdown() { - self.connection.execute { - $0.shutdown0() - } - } - - func close(promise: EventLoopPromise?) { - self.channel.close(mode: .all, promise: promise) - } - - func close() -> EventLoopFuture { - let promise = self.eventLoop.makePromise(of: Void.self) - self.close(promise: promise) - return promise.futureResult - } - } - - func _start0() -> EventLoopFuture { - self.channel.eventLoop.assertInEventLoop() - - let readyToAcceptConnectionsPromise = self.channel.eventLoop.makePromise(of: Int.self) - - self.state = .starting(readyToAcceptConnectionsPromise) - self.channel.closeFuture.assumeIsolated().whenComplete { _ in - switch self.state { - case .initialized, .closed: - preconditionFailure("invalid state \(self.state)") - case .starting(let readyToAcceptConnectionsPromise): - self.state = .closed - readyToAcceptConnectionsPromise.fail(HTTPClientError.remoteConnectionClosed) - case .active, .closing: - self.state = .closed - self.delegate.http2ConnectionClosed(self.id) - } - } - - do { - // We create and add the http handlers ourselves here, since we need to inject an - // `HTTP2IdleHandler` between the `NIOHTTP2Handler` and the `HTTP2StreamMultiplexer`. - // The purpose of the `HTTP2IdleHandler` is to count open streams in the multiplexer. - // We use the HTTP2IdleHandler's information to notify our delegate, whether more work - // can be scheduled on this connection. - let sync = self.channel.pipeline.syncOperations - - let http2Handler = NIOHTTP2Handler(mode: .client, initialSettings: Self.defaultSettings) - let idleHandler = HTTP2IdleHandler( - delegate: self, - logger: self.logger, - maximumConnectionUses: self.maximumConnectionUses - ) - - try sync.addHandler(http2Handler, position: .last) - try sync.addHandler(idleHandler, position: .last) - try sync.addHandler(self.multiplexer, position: .last) - } catch { - self.channel.close(mode: .all, promise: nil) - readyToAcceptConnectionsPromise.fail(error) - } - - return readyToAcceptConnectionsPromise.futureResult - } - - private func executeRequest0(_ request: HTTPExecutableRequest) { - self.channel.eventLoop.assertInEventLoop() - - switch self.state { - case .initialized, .starting: - preconditionFailure("Invalid state: \(self.state). Sending requests is not allowed before we are started.") - - case .active: - let createStreamChannelPromise = self.channel.eventLoop.makePromise(of: Channel.self) - let loopBoundSelf = NIOLoopBound(self, eventLoop: self.channel.eventLoop) - - self.multiplexer.createStreamChannel( - promise: createStreamChannelPromise - ) { [streamChannelDebugInitializer] channel -> EventLoopFuture in - let connection = loopBoundSelf.value - - do { - // the connection may have been asked to shutdown while we created the child. in - // this - // channel. - guard case .active = connection.state else { - throw HTTPClientError.cancelled - } - - // We only support http/2 over an https connection – using the Application-Layer - // Protocol Negotiation (ALPN). For this reason it is safe to fix this to `.https`. - let translate = HTTP2FramePayloadToHTTP1ClientCodec(httpProtocol: .https) - try channel.pipeline.syncOperations.addHandler(translate) - - if case .enabled(let limit) = connection.decompression { - let decompressHandler = NIOHTTPResponseDecompressor(limit: limit) - try channel.pipeline.syncOperations.addHandler(decompressHandler) - } - - let handler = HTTP2ClientRequestHandler(eventLoop: channel.eventLoop) - try channel.pipeline.syncOperations.addHandler(handler) - - // We must add the new channel to the list of open channels BEFORE we write the - // request to it. In case of an error, we are sure that the channel was added - // before. - let box = ChannelBox(channel) - connection.openStreams.insert(box) - channel.closeFuture.assumeIsolated().whenComplete { _ in - connection.openStreams.remove(box) - } - - if let streamChannelDebugInitializer = streamChannelDebugInitializer { - return streamChannelDebugInitializer(channel).map { _ in - channel.write(request, promise: nil) - } - } else { - channel.pipeline.syncOperations.write(NIOAny(request), promise: nil) - return channel.eventLoop.makeSucceededVoidFuture() - } - } catch { - return channel.eventLoop.makeFailedFuture(error) - } - } - - createStreamChannelPromise.futureResult.whenFailure { error in - request.fail(error) - } - - case .closing, .closed: - // Because of race conditions requests might reach this point, even though the - // connection is already closing - return request.fail(HTTPClientError.cancelled) - } - } - - private func shutdown0() { - self.channel.eventLoop.assertInEventLoop() - - switch self.state { - case .active: - self.state = .closing - - // inform all open streams, that the currently running request should be cancelled. - for box in self.openStreams { - box.channel.triggerUserOutboundEvent(HTTPConnectionEvent.shutdownRequested, promise: nil) - } - - // inform the idle connection handler, that connection should be closed, once all streams - // are closed. - self.channel.triggerUserOutboundEvent(HTTPConnectionEvent.shutdownRequested, promise: nil) - - case .closed, .closing: - // we are already closing/closed and we need to tolerate this - break - - case .initialized, .starting: - preconditionFailure("invalid state \(self.state)") - } - } - - func __forTesting_getStreamChannels() -> [Channel] { - self.channel.eventLoop.preconditionInEventLoop() - return self.openStreams.map { $0.channel } - } -} - -extension HTTP2Connection: HTTP2IdleHandlerDelegate { - func http2SettingsReceived(maxStreams: Int) { - self.channel.eventLoop.assertInEventLoop() - - switch self.state { - case .initialized: - preconditionFailure("Invalid state: \(self.state)") - - case .starting(let promise): - self.state = .active(maxStreams: maxStreams) - promise.succeed(maxStreams) - - case .active: - self.state = .active(maxStreams: maxStreams) - self.delegate.http2Connection(self.id, newMaxStreamSetting: maxStreams) - - case .closing, .closed: - // ignore. we only wait for all connections to be closed anyway. - break - } - } - - func http2GoAwayReceived() { - self.channel.eventLoop.assertInEventLoop() - - switch self.state { - case .initialized: - preconditionFailure("Invalid state: \(self.state)") - - case .starting(let promise): - self.state = .closing - promise.fail(HTTP2ReceivedGoAwayBeforeSettingsError()) - - case .active: - self.state = .closing - self.delegate.http2ConnectionGoAwayReceived(self.id) - - case .closing, .closed: - // we are already closing. Nothing new - break - } - } - - func http2StreamClosed(availableStreams: Int) { - self.channel.eventLoop.assertInEventLoop() - - self.delegate.http2ConnectionStreamClosed(self.id, availableStreams: availableStreams) - } -} - -@available(*, unavailable) -extension HTTP2Connection: Sendable {} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/HTTP2/HTTP2IdleHandler.swift b/Sources/AsyncHTTPClient/ConnectionPool/HTTP2/HTTP2IdleHandler.swift deleted file mode 100644 index 64a151489..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/HTTP2/HTTP2IdleHandler.swift +++ /dev/null @@ -1,317 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOCore -import NIOHTTP2 - -protocol HTTP2IdleHandlerDelegate { - func http2SettingsReceived(maxStreams: Int) - - func http2GoAwayReceived() - - func http2StreamClosed(availableStreams: Int) -} - -// This is a `ChannelDuplexHandler` since we need to intercept outgoing user events. It is generic -// over its delegate to allow for specialization. -final class HTTP2IdleHandler: ChannelDuplexHandler { - typealias InboundIn = HTTP2Frame - typealias InboundOut = HTTP2Frame - typealias OutboundIn = HTTP2Frame - typealias OutboundOut = HTTP2Frame - - let logger: Logger - let delegate: Delegate - - private var state: StateMachine - - init(delegate: Delegate, logger: Logger, maximumConnectionUses: Int? = nil) { - self.state = StateMachine(maximumUses: maximumConnectionUses) - self.delegate = delegate - self.logger = logger - } - - func handlerAdded(context: ChannelHandlerContext) { - if context.channel.isActive { - self.state.channelActive() - } - } - - func channelActive(context: ChannelHandlerContext) { - self.state.channelActive() - context.fireChannelActive() - } - - func channelInactive(context: ChannelHandlerContext) { - self.state.channelInactive() - context.fireChannelInactive() - } - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - let frame = self.unwrapInboundIn(data) - - switch frame.payload { - case .goAway: - let action = self.state.goAwayReceived() - self.run(action, context: context) - - case .settings(.settings(let settings)): - let action = self.state.settingsReceived(settings) - self.run(action, context: context) - - default: - // We're not interested in other events. - break - } - - context.fireChannelRead(data) - } - - func userInboundEventTriggered(context: ChannelHandlerContext, event: Any) { - // We intercept calls between the `NIOHTTP2ChannelHandler` and the `HTTP2StreamMultiplexer` - // to learn, how many open streams we have. - switch event { - case is StreamClosedEvent: - let action = self.state.streamClosed() - self.run(action, context: context) - - case is NIOHTTP2StreamCreatedEvent: - let action = self.state.streamCreated() - self.run(action, context: context) - - default: - // We're not interested in other events. - break - } - - context.fireUserInboundEventTriggered(event) - } - - func triggerUserOutboundEvent(context: ChannelHandlerContext, event: Any, promise: EventLoopPromise?) { - switch event { - case HTTPConnectionEvent.shutdownRequested: - let action = self.state.closeEventReceived() - self.run(action, context: context) - - default: - context.triggerUserOutboundEvent(event, promise: promise) - } - } - - private func run(_ action: StateMachine.Action, context: ChannelHandlerContext) { - switch action { - case .nothing: - break - - case .notifyConnectionNewMaxStreamsSettings(let maxStreams): - self.delegate.http2SettingsReceived(maxStreams: maxStreams) - - case .notifyConnectionStreamClosed(let currentlyAvailable): - self.delegate.http2StreamClosed(availableStreams: currentlyAvailable) - - case .notifyConnectionGoAwayReceived: - self.delegate.http2GoAwayReceived() - - case .close: - context.close(mode: .all, promise: nil) - } - } -} - -extension HTTP2IdleHandler { - struct StateMachine { - enum Action { - case notifyConnectionNewMaxStreamsSettings(Int) - case notifyConnectionGoAwayReceived(close: Bool) - case notifyConnectionStreamClosed(currentlyAvailable: Int) - case nothing - case close - } - - enum State { - case initialized(maximumUses: Int?) - case connected(remainingUses: Int?) - case active(openStreams: Int, maxStreams: Int, remainingUses: Int?) - case closing(openStreams: Int, maxStreams: Int) - case closed - } - - var state: State - - init(maximumUses: Int?) { - self.state = .initialized(maximumUses: maximumUses) - } - - mutating func channelActive() { - switch self.state { - case .initialized(let maximumUses): - self.state = .connected(remainingUses: maximumUses) - - case .connected, .active, .closing, .closed: - break - } - } - - mutating func channelInactive() { - switch self.state { - case .initialized, .connected, .active, .closing, .closed: - self.state = .closed - } - } - - mutating func settingsReceived(_ settings: HTTP2Settings) -> Action { - switch self.state { - case .initialized: - preconditionFailure("Invalid state: \(self.state)") - - case .connected(let remainingUses): - // a settings frame might have multiple entries for `maxConcurrentStreams`. We are - // only interested in the last value! If no `maxConcurrentStreams` is set, we assume - // the http/2 default of 100. - let maxStreams = settings.last(where: { $0.parameter == .maxConcurrentStreams })?.value ?? 100 - self.state = .active(openStreams: 0, maxStreams: maxStreams, remainingUses: remainingUses) - return .notifyConnectionNewMaxStreamsSettings(maxStreams) - - case .active(let openStreams, let maxStreams, let remainingUses): - if let newMaxStreams = settings.last(where: { $0.parameter == .maxConcurrentStreams })?.value, - newMaxStreams != maxStreams - { - self.state = .active( - openStreams: openStreams, - maxStreams: newMaxStreams, - remainingUses: remainingUses - ) - return .notifyConnectionNewMaxStreamsSettings(newMaxStreams) - } - return .nothing - - case .closing: - return .nothing - - case .closed: - // We may receive a Settings frame after we have called connection close, because of - // packages being delivered from the incoming buffer. - return .nothing - } - } - - mutating func goAwayReceived() -> Action { - switch self.state { - case .initialized: - preconditionFailure("Invalid state: \(self.state)") - - case .connected: - self.state = .closing(openStreams: 0, maxStreams: 0) - return .notifyConnectionGoAwayReceived(close: true) - - case .active(let openStreams, let maxStreams, _): - self.state = .closing(openStreams: openStreams, maxStreams: maxStreams) - return .notifyConnectionGoAwayReceived(close: openStreams == 0) - - case .closing: - return .notifyConnectionGoAwayReceived(close: false) - - case .closed: - // We may receive a GoAway frame after we have called connection close, because of - // packages being delivered from the incoming buffer. - return .nothing - } - } - - mutating func closeEventReceived() -> Action { - switch self.state { - case .initialized: - preconditionFailure("Invalid state: \(self.state)") - - case .connected: - self.state = .closing(openStreams: 0, maxStreams: 0) - return .close - - case .active(let openStreams, let maxStreams, _): - if openStreams == 0 { - self.state = .closed - return .close - } - - self.state = .closing(openStreams: openStreams, maxStreams: maxStreams) - return .nothing - - case .closed, .closing: - return .nothing - } - } - - mutating func streamCreated() -> Action { - switch self.state { - case .initialized, .connected: - preconditionFailure("Invalid state: \(self.state)") - - case .active(var openStreams, let maxStreams, let remainingUses): - openStreams += 1 - let remainingUses = remainingUses.map { $0 - 1 } - self.state = .active(openStreams: openStreams, maxStreams: maxStreams, remainingUses: remainingUses) - - if remainingUses == 0 { - // Treat running out of connection uses as if we received a GOAWAY frame. This - // will notify the delegate (i.e. connection pool) that the connection can no - // longer be used. - return self.goAwayReceived() - } else { - return .nothing - } - - case .closing(var openStreams, let maxStreams): - // A stream might be opened, while we are closing because of race conditions. For - // this reason, we should handle this case. - openStreams += 1 - self.state = .closing(openStreams: openStreams, maxStreams: maxStreams) - return .nothing - - case .closed: - // We may receive a events after we have called connection close, because of - // internal races. We should just ignore these cases. - return .nothing - } - } - - mutating func streamClosed() -> Action { - switch self.state { - case .initialized, .connected: - preconditionFailure("Invalid state: \(self.state)") - - case .active(var openStreams, let maxStreams, let remainingUses): - openStreams -= 1 - assert(openStreams >= 0) - self.state = .active(openStreams: openStreams, maxStreams: maxStreams, remainingUses: remainingUses) - return .notifyConnectionStreamClosed(currentlyAvailable: maxStreams - openStreams) - - case .closing(var openStreams, let maxStreams): - openStreams -= 1 - assert(openStreams >= 0) - if openStreams == 0 { - self.state = .closed - return .close - } - self.state = .closing(openStreams: openStreams, maxStreams: maxStreams) - return .nothing - - case .closed: - // We may receive a events after we have called connection close, because of - // internal races. We should just ignore these cases. - return .nothing - } - } - } -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/HTTPConnectionEvent.swift b/Sources/AsyncHTTPClient/ConnectionPool/HTTPConnectionEvent.swift deleted file mode 100644 index 295976732..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/HTTPConnectionEvent.swift +++ /dev/null @@ -1,17 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -enum HTTPConnectionEvent { - case shutdownRequested -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/HTTPConnectionPool+Factory.swift b/Sources/AsyncHTTPClient/ConnectionPool/HTTPConnectionPool+Factory.swift deleted file mode 100644 index c896791cf..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/HTTPConnectionPool+Factory.swift +++ /dev/null @@ -1,644 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOCore -import NIOHTTP1 -import NIOHTTPCompression -import NIOPosix -import NIOSOCKS -import NIOSSL -import NIOTLS - -#if canImport(Network) -import Network -import NIOTransportServices -#endif - -extension HTTPConnectionPool { - struct ConnectionFactory { - let key: ConnectionPool.Key - let clientConfiguration: HTTPClient.Configuration - let tlsConfiguration: TLSConfiguration - let sslContextCache: SSLContextCache - - init( - key: ConnectionPool.Key, - tlsConfiguration: TLSConfiguration?, - clientConfiguration: HTTPClient.Configuration, - sslContextCache: SSLContextCache - ) { - self.key = key - self.clientConfiguration = clientConfiguration - self.sslContextCache = sslContextCache - self.tlsConfiguration = - tlsConfiguration ?? clientConfiguration.tlsConfiguration ?? .makeClientConfiguration() - } - } -} - -protocol HTTPConnectionRequester: Sendable { - func http1ConnectionCreated(_: HTTP1Connection.SendableView) - func http2ConnectionCreated(_: HTTP2Connection.SendableView, maximumStreams: Int) - func failedToCreateHTTPConnection(_: HTTPConnectionPool.Connection.ID, error: Error) - func waitingForConnectivity(_: HTTPConnectionPool.Connection.ID, error: Error) -} - -extension HTTPConnectionPool.ConnectionFactory { - func makeConnection( - for requester: Requester, - connectionID: HTTPConnectionPool.Connection.ID, - http1ConnectionDelegate: HTTP1ConnectionDelegate, - http2ConnectionDelegate: HTTP2ConnectionDelegate, - deadline: NIODeadline, - eventLoop: EventLoop, - logger: Logger - ) { - var logger = logger - logger[metadataKey: "ahc-connection-id"] = "\(connectionID)" - - self.makeChannel( - requester: requester, - connectionID: connectionID, - deadline: deadline, - eventLoop: eventLoop, - logger: logger - ).whenComplete { [logger] result in - switch result { - case .success(.http1_1(let channel)): - do { - let connection = try HTTP1Connection.start( - channel: channel, - connectionID: connectionID, - delegate: http1ConnectionDelegate, - decompression: self.clientConfiguration.decompression, - logger: logger - ) - - if let connectionDebugInitializer = self.clientConfiguration.http1_1ConnectionDebugInitializer { - connectionDebugInitializer(channel).hop( - to: eventLoop - ).assumeIsolated().whenComplete { debugInitializerResult in - switch debugInitializerResult { - case .success: - requester.http1ConnectionCreated(connection.sendableView) - case .failure(let error): - requester.failedToCreateHTTPConnection(connectionID, error: error) - } - } - } else { - requester.http1ConnectionCreated(connection.sendableView) - } - } catch { - requester.failedToCreateHTTPConnection(connectionID, error: error) - } - case .success(.http2(let channel)): - HTTP2Connection.start( - channel: channel, - connectionID: connectionID, - delegate: http2ConnectionDelegate, - decompression: self.clientConfiguration.decompression, - maximumConnectionUses: self.clientConfiguration.maximumUsesPerConnection, - logger: logger, - streamChannelDebugInitializer: - self.clientConfiguration.http2StreamChannelDebugInitializer - ).whenComplete { result in - switch result { - case .success((let connection, let maximumStreams)): - if let connectionDebugInitializer = self.clientConfiguration.http2ConnectionDebugInitializer { - connectionDebugInitializer(channel).hop(to: eventLoop).assumeIsolated().whenComplete { - debugInitializerResult in - switch debugInitializerResult { - case .success: - requester.http2ConnectionCreated( - connection.sendableView, - maximumStreams: maximumStreams - ) - case .failure(let error): - requester.failedToCreateHTTPConnection( - connectionID, - error: error - ) - } - } - } else { - requester.http2ConnectionCreated( - connection.sendableView, - maximumStreams: maximumStreams - ) - } - case .failure(let error): - requester.failedToCreateHTTPConnection(connectionID, error: error) - } - } - - case .failure(let error): - requester.failedToCreateHTTPConnection(connectionID, error: error) - } - } - } - - enum NegotiatedProtocol { - case http1_1(Channel) - case http2(Channel) - } - - func makeChannel( - requester: Requester, - connectionID: HTTPConnectionPool.Connection.ID, - deadline: NIODeadline, - eventLoop: EventLoop, - logger: Logger - ) -> EventLoopFuture { - let channelFuture: EventLoopFuture - - if self.key.scheme.isProxyable, let proxy = self.clientConfiguration.proxy { - switch proxy.type { - case .socks: - channelFuture = self.makeSOCKSProxyChannel( - proxy, - requester: requester, - connectionID: connectionID, - deadline: deadline, - eventLoop: eventLoop, - logger: logger - ) - case .http: - channelFuture = self.makeHTTPProxyChannel( - proxy, - requester: requester, - connectionID: connectionID, - deadline: deadline, - eventLoop: eventLoop, - logger: logger - ) - } - } else { - channelFuture = self.makeNonProxiedChannel( - requester: requester, - connectionID: connectionID, - deadline: deadline, - eventLoop: eventLoop, - logger: logger - ) - } - - // let's map `ChannelError.connectTimeout` into a `HTTPClientError.connectTimeout` - return channelFuture.flatMapErrorThrowing { error throws -> NegotiatedProtocol in - switch error { - case ChannelError.connectTimeout: - throw HTTPClientError.connectTimeout - default: - throw error - } - } - } - - private func makeNonProxiedChannel( - requester: Requester, - connectionID: HTTPConnectionPool.Connection.ID, - deadline: NIODeadline, - eventLoop: EventLoop, - logger: Logger - ) -> EventLoopFuture { - switch self.key.scheme { - case .http, .httpUnix, .unix: - return self.makePlainChannel( - requester: requester, - connectionID: connectionID, - deadline: deadline, - eventLoop: eventLoop - ).map { .http1_1($0) } - case .https, .httpsUnix: - return self.makeTLSChannel( - requester: requester, - connectionID: connectionID, - deadline: deadline, - eventLoop: eventLoop, - logger: logger - ).flatMapThrowing { - channel, - negotiated in - - try self.matchALPNToHTTPVersion(negotiated, channel: channel) - } - } - } - - private func makePlainChannel( - requester: Requester, - connectionID: HTTPConnectionPool.Connection.ID, - deadline: NIODeadline, - eventLoop: EventLoop - ) -> EventLoopFuture { - precondition(!self.key.scheme.usesTLS, "Unexpected scheme") - return self.makePlainBootstrap( - requester: requester, - connectionID: connectionID, - deadline: deadline, - eventLoop: eventLoop - ).connect(target: self.key.connectionTarget) - } - - private func makeHTTPProxyChannel( - _ proxy: HTTPClient.Configuration.Proxy, - requester: Requester, - connectionID: HTTPConnectionPool.Connection.ID, - deadline: NIODeadline, - eventLoop: EventLoop, - logger: Logger - ) -> EventLoopFuture { - // A proxy connection starts with a plain text connection to the proxy server. After - // the connection has been established with the proxy server, the connection might be - // upgraded to TLS before we send our first request. - let bootstrap = self.makePlainBootstrap( - requester: requester, - connectionID: connectionID, - deadline: deadline, - eventLoop: eventLoop - ) - return bootstrap.connect(host: proxy.host, port: proxy.port).flatMap { channel in - let encoder = HTTPRequestEncoder() - let decoder = ByteToMessageHandler(HTTPResponseDecoder(leftOverBytesStrategy: .dropBytes)) - let proxyHandler = HTTP1ProxyConnectHandler( - target: self.key.connectionTarget, - proxyAuthorization: proxy.authorization, - deadline: deadline - ) - - do { - try channel.pipeline.syncOperations.addHandler(encoder) - try channel.pipeline.syncOperations.addHandler(decoder) - try channel.pipeline.syncOperations.addHandler(proxyHandler) - } catch { - return channel.eventLoop.makeFailedFuture(error) - } - - // The proxyEstablishedFuture is set as soon as the HTTP1ProxyConnectHandler is in a - // pipeline. It is created in HTTP1ProxyConnectHandler's handlerAdded method. - return proxyHandler.proxyEstablishedFuture!.assumeIsolated().flatMap { - channel.pipeline.syncOperations.removeHandler(proxyHandler).assumeIsolated().flatMap { - channel.pipeline.syncOperations.removeHandler(decoder).assumeIsolated().flatMap { - channel.pipeline.syncOperations.removeHandler(encoder) - }.nonisolated() - }.nonisolated() - }.flatMap { - self.setupTLSInProxyConnectionIfNeeded(channel, deadline: deadline, logger: logger) - }.nonisolated() - } - } - - private func makeSOCKSProxyChannel( - _ proxy: HTTPClient.Configuration.Proxy, - requester: Requester, - connectionID: HTTPConnectionPool.Connection.ID, - deadline: NIODeadline, - eventLoop: EventLoop, - logger: Logger - ) -> EventLoopFuture { - // A proxy connection starts with a plain text connection to the proxy server. After - // the connection has been established with the proxy server, the connection might be - // upgraded to TLS before we send our first request. - let bootstrap = self.makePlainBootstrap( - requester: requester, - connectionID: connectionID, - deadline: deadline, - eventLoop: eventLoop - ) - return bootstrap.connect(host: proxy.host, port: proxy.port).flatMap { channel in - let socksConnectHandler = SOCKSClientHandler(targetAddress: SOCKSAddress(self.key.connectionTarget)) - let socksEventHandler = SOCKSEventsHandler(deadline: deadline) - - do { - try channel.pipeline.syncOperations.addHandler(socksConnectHandler) - try channel.pipeline.syncOperations.addHandler(socksEventHandler) - } catch { - return channel.eventLoop.makeFailedFuture(error) - } - - // The socksEstablishedFuture is set as soon as the SOCKSEventsHandler is in a - // pipeline. It is created in SOCKSEventsHandler's handlerAdded method. - return socksEventHandler.socksEstablishedFuture!.assumeIsolated().flatMap { - channel.pipeline.syncOperations.removeHandler(socksEventHandler).assumeIsolated().flatMap { - channel.pipeline.syncOperations.removeHandler(socksConnectHandler) - }.nonisolated() - }.flatMap { - self.setupTLSInProxyConnectionIfNeeded(channel, deadline: deadline, logger: logger) - }.nonisolated() - } - } - - private func setupTLSInProxyConnectionIfNeeded( - _ channel: Channel, - deadline: NIODeadline, - logger: Logger - ) -> EventLoopFuture { - switch self.key.scheme { - case .unix, .httpUnix, .httpsUnix: - preconditionFailure("Unexpected scheme. Not supported for proxy!") - case .http: - return channel.eventLoop.makeSucceededFuture(.http1_1(channel)) - case .https: - var tlsConfig = self.tlsConfiguration - switch self.clientConfiguration.httpVersion.configuration { - case .automatic: - // since we can support h2, we need to advertise this in alpn - // "ProtocolNameList" contains the list of protocols advertised by the - // client, in descending order of preference. - // https://datatracker.ietf.org/doc/html/rfc7301#section-3.1 - tlsConfig.applicationProtocols = ["h2", "http/1.1"] - case .http1Only: - tlsConfig.applicationProtocols = ["http/1.1"] - } - - let sslServerHostname = self.key.serverNameIndicator - let sslContextFuture = self.sslContextCache.sslContext( - tlsConfiguration: tlsConfig, - eventLoop: channel.eventLoop, - logger: logger - ) - - return sslContextFuture.flatMap { sslContext -> EventLoopFuture in - do { - let sslHandler = try NIOSSLClientHandler( - context: sslContext, - serverHostname: sslServerHostname - ) - try channel.pipeline.syncOperations.addHandler(sslHandler) - let tlsEventHandler = TLSEventsHandler(deadline: deadline) - try channel.pipeline.syncOperations.addHandler(tlsEventHandler) - - // The tlsEstablishedFuture is set as soon as the TLSEventsHandler is in a - // pipeline. It is created in TLSEventsHandler's handlerAdded method. - return tlsEventHandler.tlsEstablishedFuture! - } catch { - return channel.eventLoop.makeFailedFuture(error) - } - }.flatMap { negotiated -> EventLoopFuture in - do { - let sync = channel.pipeline.syncOperations - let context = try sync.context(handlerType: TLSEventsHandler.self) - return sync.removeHandler(context: context).flatMapThrowing { - try self.matchALPNToHTTPVersion(negotiated, channel: channel) - } - } catch { - return channel.eventLoop.makeFailedFuture(error) - } - } - } - } - - private func makePlainBootstrap( - requester: Requester, - connectionID: HTTPConnectionPool.Connection.ID, - deadline: NIODeadline, - eventLoop: EventLoop - ) -> NIOClientTCPBootstrapProtocol { - #if canImport(Network) - if #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *), - let tsBootstrap = NIOTSConnectionBootstrap(validatingGroup: eventLoop) - { - return - tsBootstrap - .channelOption( - NIOTSChannelOptions.waitForActivity, - value: self.clientConfiguration.networkFrameworkWaitForConnectivity - ) - .channelOption( - NIOTSChannelOptions.multipathServiceType, - value: self.clientConfiguration.enableMultipath ? .handover : .disabled - ) - .connectTimeout(deadline - NIODeadline.now()) - .channelInitializer { channel in - do { - try channel.pipeline.syncOperations.addHandler(HTTPClient.NWErrorHandler()) - try channel.pipeline.syncOperations.addHandler( - NWWaitingHandler(requester: requester, connectionID: connectionID) - ) - return channel.eventLoop.makeSucceededVoidFuture() - } catch { - return channel.eventLoop.makeFailedFuture(error) - } - } - } - #endif - - if let nioBootstrap = ClientBootstrap(validatingGroup: eventLoop) { - return - nioBootstrap - .connectTimeout(deadline - NIODeadline.now()) - .enableMPTCP(clientConfiguration.enableMultipath) - } - - preconditionFailure("No matching bootstrap found") - } - - private func makeTLSChannel( - requester: Requester, - connectionID: HTTPConnectionPool.Connection.ID, - deadline: NIODeadline, - eventLoop: EventLoop, - logger: Logger - ) -> EventLoopFuture<(Channel, String?)> { - precondition(self.key.scheme.usesTLS, "Unexpected scheme") - let bootstrapFuture = self.makeTLSBootstrap( - requester: requester, - connectionID: connectionID, - deadline: deadline, - eventLoop: eventLoop, - logger: logger - ) - - var channelFuture = bootstrapFuture.flatMap { bootstrap -> EventLoopFuture in - bootstrap.connect(target: self.key.connectionTarget) - }.flatMap { channel -> EventLoopFuture<(Channel, String?)> in - do { - // if the channel is closed before flatMap is executed, all ChannelHandler are removed - // and TLSEventsHandler is therefore not present either - let tlsEventHandler = try channel.pipeline.syncOperations.handler(type: TLSEventsHandler.self) - - // The tlsEstablishedFuture is set as soon as the TLSEventsHandler is in a - // pipeline. It is created in TLSEventsHandler's handlerAdded method. - return tlsEventHandler.tlsEstablishedFuture!.assumeIsolated().flatMap { negotiated in - channel.pipeline.syncOperations.removeHandler(tlsEventHandler).map { (channel, negotiated) } - }.nonisolated() - } catch { - assert( - channel.isActive == false, - "if the channel is still active then TLSEventsHandler must be present but got error \(error)" - ) - return channel.eventLoop.makeFailedFuture(HTTPClientError.remoteConnectionClosed) - } - } - - #if canImport(Network) - // If NIOTransportSecurity is used, we want to map NWErrors into NWPOsixErrors or NWTLSError. - channelFuture = channelFuture.flatMapErrorThrowing { error in - throw HTTPClient.NWErrorHandler.translateError(error) - } - #endif - - return channelFuture - } - - private func makeTLSBootstrap( - requester: Requester, - connectionID: HTTPConnectionPool.Connection.ID, - deadline: NIODeadline, - eventLoop: EventLoop, - logger: Logger - ) -> EventLoopFuture { - var tlsConfig = self.tlsConfiguration - switch self.clientConfiguration.httpVersion.configuration { - case .automatic: - // since we can support h2, we need to advertise this in alpn - // "ProtocolNameList" contains the list of protocols advertised by the - // client, in descending order of preference. - // https://datatracker.ietf.org/doc/html/rfc7301#section-3.1 - tlsConfig.applicationProtocols = ["h2", "http/1.1"] - case .http1Only: - tlsConfig.applicationProtocols = ["http/1.1"] - } - - #if canImport(Network) - if #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *), eventLoop is QoSEventLoop { - // create NIOClientTCPBootstrap with NIOTS TLS provider - let bootstrapFuture = tlsConfig.getNWProtocolTLSOptions( - on: eventLoop, - serverNameIndicatorOverride: key.serverNameIndicatorOverride - ).map { - options -> NIOClientTCPBootstrapProtocol in - - NIOTSConnectionBootstrap(group: eventLoop) // validated above - .channelOption( - NIOTSChannelOptions.waitForActivity, - value: self.clientConfiguration.networkFrameworkWaitForConnectivity - ) - .channelOption( - NIOTSChannelOptions.multipathServiceType, - value: self.clientConfiguration.enableMultipath ? .handover : .disabled - ) - .connectTimeout(deadline - NIODeadline.now()) - .tlsOptions(options) - .channelInitializer { channel in - do { - try channel.pipeline.syncOperations.addHandler(HTTPClient.NWErrorHandler()) - try channel.pipeline.syncOperations.addHandler( - NWWaitingHandler(requester: requester, connectionID: connectionID) - ) - // we don't need to set a TLS deadline for NIOTS connections, since the - // TLS handshake is part of the TS connection bootstrap. If the TLS - // handshake times out the complete connection creation will be failed. - try channel.pipeline.syncOperations.addHandler(TLSEventsHandler(deadline: nil)) - return channel.eventLoop.makeSucceededVoidFuture() - } catch { - return channel.eventLoop.makeFailedFuture(error) - } - } as NIOClientTCPBootstrapProtocol - } - return bootstrapFuture - } - #endif - - let sslContextFuture = sslContextCache.sslContext( - tlsConfiguration: tlsConfig, - eventLoop: eventLoop, - logger: logger - ) - - return eventLoop.submit { - ClientBootstrap(group: eventLoop) - .connectTimeout(deadline - NIODeadline.now()) - .enableMPTCP(clientConfiguration.enableMultipath) - .channelInitializer { channel in - sslContextFuture.flatMap { sslContext -> EventLoopFuture in - do { - let sync = channel.pipeline.syncOperations - let sslHandler = try NIOSSLClientHandler( - context: sslContext, - serverHostname: self.key.serverNameIndicator - ) - let tlsEventHandler = TLSEventsHandler(deadline: deadline) - - try sync.addHandler(sslHandler) - try sync.addHandler(tlsEventHandler) - return channel.eventLoop.makeSucceededVoidFuture() - } catch { - return channel.eventLoop.makeFailedFuture(error) - } - } - } - } - } - - private func matchALPNToHTTPVersion(_ negotiated: String?, channel: Channel) throws -> NegotiatedProtocol { - switch negotiated { - case .none, .some("http/1.1"): - return .http1_1(channel) - case .some("h2"): - return .http2(channel) - case .some(let unsupported): - throw HTTPClientError.serverOfferedUnsupportedApplicationProtocol(unsupported) - } - } -} - -extension Scheme { - var isProxyable: Bool { - switch self { - case .http, .https: - return true - case .unix, .httpUnix, .httpsUnix: - return false - } - } -} - -extension ConnectionPool.Key { - var serverNameIndicator: String? { - serverNameIndicatorOverride ?? connectionTarget.sslServerHostname - } -} - -extension ConnectionTarget { - fileprivate var sslServerHostname: String? { - switch self { - case .domain(let domain, _): return domain - case .ipAddress, .unixSocket: return nil - } - } -} - -extension SOCKSAddress { - fileprivate init(_ host: ConnectionTarget) { - switch host { - case .ipAddress(_, let address): self = .address(address) - case .domain(let domain, let port): self = .domain(domain, port: port) - case .unixSocket: fatalError("Unix Domain Sockets are not supported by SOCKSAddress") - } - } -} - -extension NIOClientTCPBootstrapProtocol { - func connect(target: ConnectionTarget) -> EventLoopFuture { - switch target { - case .ipAddress(_, let socketAddress): - return self.connect(to: socketAddress) - case .domain(let domain, let port): - return self.connect(host: domain, port: port) - case .unixSocket(let path): - return self.connect(unixDomainSocketPath: path) - } - } -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/HTTPConnectionPool+Manager.swift b/Sources/AsyncHTTPClient/ConnectionPool/HTTPConnectionPool+Manager.swift deleted file mode 100644 index 3fdf93752..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/HTTPConnectionPool+Manager.swift +++ /dev/null @@ -1,183 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Atomics -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOHTTP1 - -extension HTTPConnectionPool { - final class Manager { - private typealias Key = ConnectionPool.Key - - private enum State { - case active - case shuttingDown(promise: EventLoopPromise?, unclean: Bool) - case shutDown - } - - private let eventLoopGroup: EventLoopGroup - private let configuration: HTTPClient.Configuration - private let connectionIDGenerator = Connection.ID.globalGenerator - private let logger: Logger - - private var state: State = .active - private var _pools: [Key: HTTPConnectionPool] = [:] - private let lock = NIOLock() - - private let sslContextCache = SSLContextCache() - - init( - eventLoopGroup: EventLoopGroup, - configuration: HTTPClient.Configuration, - backgroundActivityLogger logger: Logger - ) { - self.eventLoopGroup = eventLoopGroup - self.configuration = configuration - self.logger = logger - } - - func executeRequest(_ request: HTTPSchedulableRequest) { - let poolKey = request.poolKey - let poolResult = self.lock.withLock { () -> Result in - switch self.state { - case .active: - if let pool = self._pools[poolKey] { - return .success(pool) - } - - let pool = HTTPConnectionPool( - eventLoopGroup: self.eventLoopGroup, - sslContextCache: self.sslContextCache, - tlsConfiguration: request.tlsConfiguration, - clientConfiguration: self.configuration, - key: poolKey, - delegate: self, - idGenerator: self.connectionIDGenerator, - backgroundActivityLogger: self.logger - ) - self._pools[poolKey] = pool - return .success(pool) - - case .shuttingDown, .shutDown: - return .failure(HTTPClientError.alreadyShutdown) - } - } - - switch poolResult { - case .success(let pool): - pool.executeRequest(request) - case .failure(let error): - request.fail(error) - } - } - - /// Shutdown the connection pool manager. You **must** shutdown the pool manager, since it leak otherwise. - /// - /// - Parameter promise: An `EventLoopPromise` that is succeeded once all connections pools are shutdown. - /// - Returns: An EventLoopFuture that is succeeded once the pool is shutdown. The bool indicates if the - /// shutdown was unclean. - func shutdown(promise: EventLoopPromise?) { - enum ShutdownAction { - case done(EventLoopPromise?) - case shutdown([Key: HTTPConnectionPool]) - } - - let action = self.lock.withLock { () -> ShutdownAction in - switch self.state { - case .active: - // If there aren't any pools, we can mark the pool as shut down right away. - if self._pools.isEmpty { - self.state = .shutDown - return .done(promise) - } else { - // this promise will be succeeded once all connection pools are shutdown - self.state = .shuttingDown(promise: promise, unclean: false) - return .shutdown(self._pools) - } - - case .shuttingDown, .shutDown: - preconditionFailure("PoolManager already shutdown") - } - } - - // if no pools are returned, the manager is already shutdown completely. Inform the - // delegate. This is a very clean shutdown... - switch action { - case .done(let promise): - promise?.succeed(false) - - case .shutdown(let pools): - for pool in pools.values { - pool.shutdown() - } - } - } - } -} - -extension HTTPConnectionPool.Manager: HTTPConnectionPoolDelegate { - func connectionPoolDidShutdown(_ pool: HTTPConnectionPool, unclean: Bool) { - enum CloseAction { - case close(EventLoopPromise?, unclean: Bool) - case wait - } - - let closeAction = self.lock.withLock { () -> CloseAction in - switch self.state { - case .active, .shutDown: - preconditionFailure("Why are pools shutting down, if the manager did not give a signal") - - case .shuttingDown(let promise, let soFarUnclean): - guard self._pools.removeValue(forKey: pool.key) === pool else { - preconditionFailure( - "Expected that the pool was created by this manager and is known for this reason." - ) - } - - if self._pools.isEmpty { - self.state = .shutDown - return .close(promise, unclean: soFarUnclean || unclean) - } else { - self.state = .shuttingDown(promise: promise, unclean: soFarUnclean || unclean) - return .wait - } - } - } - - switch closeAction { - case .close(let promise, let unclean): - promise?.succeed(unclean) - case .wait: - break - } - } -} - -extension HTTPConnectionPool.Connection.ID { - static let globalGenerator = Generator() - - struct Generator { - private let atomic: ManagedAtomic - - init() { - self.atomic = .init(0) - } - - func next() -> Int { - self.atomic.loadThenWrappingIncrement(ordering: .relaxed) - } - } -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/HTTPConnectionPool.swift b/Sources/AsyncHTTPClient/ConnectionPool/HTTPConnectionPool.swift deleted file mode 100644 index 251224ac0..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/HTTPConnectionPool.swift +++ /dev/null @@ -1,787 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOSSL - -protocol HTTPConnectionPoolDelegate { - func connectionPoolDidShutdown(_ pool: HTTPConnectionPool, unclean: Bool) -} - -final class HTTPConnectionPool: - // TODO: Refactor to use `NIOLockedValueBox` which will allow this to be checked - @unchecked Sendable -{ - private let stateLock = NIOLock() - private var _state: StateMachine - /// The connection idle timeout timers. Protected by the stateLock - private var _idleTimer = [Connection.ID: Scheduled]() - /// The connection backoff timeout timers. Protected by the stateLock - private var _backoffTimer = [Connection.ID: Scheduled]() - /// The request connection timeout timers. Protected by the stateLock - private var _requestTimer = [Request.ID: Scheduled]() - - private static let fallbackConnectTimeout: TimeAmount = .seconds(30) - - let key: ConnectionPool.Key - - private var logger: Logger - - private let eventLoopGroup: EventLoopGroup - private let connectionFactory: ConnectionFactory - private let clientConfiguration: HTTPClient.Configuration - private let idleConnectionTimeout: TimeAmount - - let delegate: HTTPConnectionPoolDelegate - - init( - eventLoopGroup: EventLoopGroup, - sslContextCache: SSLContextCache, - tlsConfiguration: TLSConfiguration?, - clientConfiguration: HTTPClient.Configuration, - key: ConnectionPool.Key, - delegate: HTTPConnectionPoolDelegate, - idGenerator: Connection.ID.Generator, - backgroundActivityLogger logger: Logger - ) { - self.eventLoopGroup = eventLoopGroup - self.connectionFactory = ConnectionFactory( - key: key, - tlsConfiguration: tlsConfiguration, - clientConfiguration: clientConfiguration, - sslContextCache: sslContextCache - ) - self.clientConfiguration = clientConfiguration - self.key = key - self.delegate = delegate - var logger = logger - logger[metadataKey: "ahc-pool-key"] = "\(key)" - self.logger = logger - - self.idleConnectionTimeout = clientConfiguration.connectionPool.idleTimeout - - self._state = StateMachine( - idGenerator: idGenerator, - maximumConcurrentHTTP1Connections: clientConfiguration.connectionPool - .concurrentHTTP1ConnectionsPerHostSoftLimit, - retryConnectionEstablishment: clientConfiguration.connectionPool.retryConnectionEstablishment, - preferHTTP1: clientConfiguration.httpVersion == .http1Only, - maximumConnectionUses: clientConfiguration.maximumUsesPerConnection - ) - } - - func executeRequest(_ request: HTTPSchedulableRequest) { - self.modifyStateAndRunActions { $0.executeRequest(.init(request)) } - } - - func shutdown() { - self.logger.debug("Shutting down connection pool") - self.modifyStateAndRunActions { $0.shutdown() } - } - - // MARK: - Private Methods - - - // MARK: Actions - - /// An `HTTPConnectionPool` internal action type that matches the `StateMachine`'s action. - /// However it splits up the actions into actions that need to be executed inside the `stateLock` - /// and outside the `stateLock`. - private struct Actions { - enum ConnectionAction { - enum Unlocked { - case createConnection(Connection.ID, on: EventLoop) - case closeConnection(Connection, isShutdown: StateMachine.ConnectionAction.IsShutdown) - case cleanupConnections(CleanupContext, isShutdown: StateMachine.ConnectionAction.IsShutdown) - case migration( - createConnections: [(Connection.ID, EventLoop)], - closeConnections: [Connection] - ) - case none - } - - enum Locked { - case scheduleBackoffTimer(Connection.ID, backoff: TimeAmount, on: EventLoop) - case cancelBackoffTimers([Connection.ID]) - case scheduleTimeoutTimer(Connection.ID, on: EventLoop) - case cancelTimeoutTimer(Connection.ID) - case none - } - } - - enum RequestAction { - enum Unlocked { - case executeRequest(Request, Connection) - case executeRequests([Request], Connection) - case failRequest(Request, Error) - case failRequests([Request], Error) - case none - } - - enum Locked { - case scheduleRequestTimeout(for: Request, on: EventLoop) - case cancelRequestTimeout(Request.ID) - case cancelRequestTimeouts([Request]) - case none - } - } - - struct Locked { - var connection: ConnectionAction.Locked - var request: RequestAction.Locked - } - - struct Unlocked { - var connection: ConnectionAction.Unlocked - var request: RequestAction.Unlocked - } - - var locked: Locked - var unlocked: Unlocked - - init(from stateMachineAction: StateMachine.Action) { - self.locked = Locked(connection: .none, request: .none) - self.unlocked = Unlocked(connection: .none, request: .none) - - switch stateMachineAction.request { - case .executeRequest(let request, let connection, let cancelTimeout): - if cancelTimeout { - self.locked.request = .cancelRequestTimeout(request.id) - } - self.unlocked.request = .executeRequest(request, connection) - case .executeRequestsAndCancelTimeouts(let requests, let connection): - self.locked.request = .cancelRequestTimeouts(requests) - self.unlocked.request = .executeRequests(requests, connection) - case .failRequest(let request, let error, let cancelTimeout): - if cancelTimeout { - self.locked.request = .cancelRequestTimeout(request.id) - } - self.unlocked.request = .failRequest(request, error) - case .failRequestsAndCancelTimeouts(let requests, let error): - self.locked.request = .cancelRequestTimeouts(requests) - self.unlocked.request = .failRequests(requests, error) - case .scheduleRequestTimeout(for: let request, on: let eventLoop): - self.locked.request = .scheduleRequestTimeout(for: request, on: eventLoop) - case .none: - break - } - - switch stateMachineAction.connection { - case .createConnection(let connectionID, on: let eventLoop): - self.unlocked.connection = .createConnection(connectionID, on: eventLoop) - case .scheduleBackoffTimer(let connectionID, let backoff, on: let eventLoop): - self.locked.connection = .scheduleBackoffTimer(connectionID, backoff: backoff, on: eventLoop) - case .scheduleTimeoutTimer(let connectionID, on: let eventLoop): - self.locked.connection = .scheduleTimeoutTimer(connectionID, on: eventLoop) - case .cancelTimeoutTimer(let connectionID): - self.locked.connection = .cancelTimeoutTimer(connectionID) - case .closeConnection(let connection, let isShutdown): - self.unlocked.connection = .closeConnection(connection, isShutdown: isShutdown) - case .cleanupConnections(var cleanupContext, let isShutdown): - // - self.locked.connection = .cancelBackoffTimers(cleanupContext.connectBackoff) - cleanupContext.connectBackoff = [] - self.unlocked.connection = .cleanupConnections(cleanupContext, isShutdown: isShutdown) - case .migration( - let createConnections, - let closeConnections, - let scheduleTimeout - ): - if let (connectionID, eventLoop) = scheduleTimeout { - self.locked.connection = .scheduleTimeoutTimer(connectionID, on: eventLoop) - } - self.unlocked.connection = .migration( - createConnections: createConnections, - closeConnections: closeConnections - ) - case .none: - break - } - } - } - - // MARK: Run actions - - private func modifyStateAndRunActions(_ closure: (inout StateMachine) -> StateMachine.Action) { - let unlockedActions = self.stateLock.withLock { () -> Actions.Unlocked in - let stateMachineAction = closure(&self._state) - let poolAction = Actions(from: stateMachineAction) - self.runLockedConnectionAction(poolAction.locked.connection) - self.runLockedRequestAction(poolAction.locked.request) - return poolAction.unlocked - } - self.runUnlockedActions(unlockedActions) - } - - private func runLockedConnectionAction(_ action: Actions.ConnectionAction.Locked) { - switch action { - case .scheduleBackoffTimer(let connectionID, let backoff, on: let eventLoop): - self.scheduleConnectionStartBackoffTimer(connectionID, backoff, on: eventLoop) - - case .scheduleTimeoutTimer(let connectionID, on: let eventLoop): - self.scheduleIdleTimerForConnection(connectionID, on: eventLoop) - - case .cancelTimeoutTimer(let connectionID): - self.cancelIdleTimerForConnection(connectionID) - - case .cancelBackoffTimers(let connectionIDs): - for connectionID in connectionIDs { - self.cancelConnectionStartBackoffTimer(connectionID) - } - - case .none: - break - } - } - - private func runLockedRequestAction(_ action: Actions.RequestAction.Locked) { - switch action { - case .scheduleRequestTimeout(for: let request, on: let eventLoop): - self.scheduleRequestTimeout(request, on: eventLoop) - - case .cancelRequestTimeout(let requestID): - self.cancelRequestTimeout(requestID) - - case .cancelRequestTimeouts(let requests): - for request in requests { self.cancelRequestTimeout(request.id) } - - case .none: - break - } - } - - private func runUnlockedActions(_ actions: Actions.Unlocked) { - self.runUnlockedConnectionAction(actions.connection) - self.runUnlockedRequestAction(actions.request) - } - - private func runUnlockedConnectionAction(_ action: Actions.ConnectionAction.Unlocked) { - switch action { - case .createConnection(let connectionID, let eventLoop): - self.createConnection(connectionID, on: eventLoop) - - case .closeConnection(let connection, let isShutdown): - self.logger.trace( - "close connection", - metadata: [ - "ahc-connection-id": "\(connection.id)" - ] - ) - - // we are not interested in the close promise... - connection.close(promise: nil) - - if case .yes(let unclean) = isShutdown { - self.delegate.connectionPoolDidShutdown(self, unclean: unclean) - } - - case .cleanupConnections(let cleanupContext, let isShutdown): - for connection in cleanupContext.close { - connection.close(promise: nil) - } - - for connection in cleanupContext.cancel { - connection.shutdown() - } - - for connectionID in cleanupContext.connectBackoff { - self.cancelConnectionStartBackoffTimer(connectionID) - } - - if case .yes(let unclean) = isShutdown { - self.delegate.connectionPoolDidShutdown(self, unclean: unclean) - } - - case .migration(let createConnections, let closeConnections): - for connection in closeConnections { - connection.close(promise: nil) - } - - for (connectionID, eventLoop) in createConnections { - self.createConnection(connectionID, on: eventLoop) - } - - case .none: - break - } - } - - private func runUnlockedRequestAction(_ action: Actions.RequestAction.Unlocked) { - switch action { - case .executeRequest(let request, let connection): - connection.executeRequest(request.req) - - case .executeRequests(let requests, let connection): - for request in requests { - connection.executeRequest(request.req) - } - - case .failRequest(let request, let error): - request.req.fail(error) - - case .failRequests(let requests, let error): - for request in requests { request.req.fail(error) } - - case .none: - break - } - } - - private func createConnection(_ connectionID: Connection.ID, on eventLoop: EventLoop) { - self.logger.trace( - "Opening fresh connection", - metadata: [ - "ahc-connection-id": "\(connectionID)" - ] - ) - // Even though this function is called make it actually creates/establishes a connection. - // TBD: Should we rename it? To what? - self.connectionFactory.makeConnection( - for: self, - connectionID: connectionID, - http1ConnectionDelegate: self, - http2ConnectionDelegate: self, - deadline: .now() + (self.clientConfiguration.timeout.connect ?? Self.fallbackConnectTimeout), - eventLoop: eventLoop, - logger: self.logger - ) - } - - private func scheduleRequestTimeout(_ request: Request, on eventLoop: EventLoop) { - let requestID = request.id - let scheduled = eventLoop.scheduleTask(deadline: request.connectionDeadline) { - // there might be a race between a the timeout timer and the pool scheduling the - // request on another thread. - self.modifyStateAndRunActions { stateMachine in - if self._requestTimer.removeValue(forKey: requestID) != nil { - // The timer still exists. State Machines assumes it is alive. Inform state - // machine. - return stateMachine.timeoutRequest(requestID) - } - return .none - } - } - - assert(self._requestTimer[requestID] == nil) - self._requestTimer[requestID] = scheduled - - request.req.requestWasQueued(self) - } - - private func cancelRequestTimeout(_ id: Request.ID) { - guard let cancelTimer = self._requestTimer.removeValue(forKey: id) else { - preconditionFailure("Expected to have a timer for request \(id) at this point.") - } - cancelTimer.cancel() - } - - private func scheduleIdleTimerForConnection(_ connectionID: Connection.ID, on eventLoop: EventLoop) { - self.logger.trace( - "Schedule idle connection timeout timer", - metadata: [ - "ahc-connection-id": "\(connectionID)" - ] - ) - let scheduled = eventLoop.scheduleTask(in: self.idleConnectionTimeout) { - // there might be a race between a cancelTimer call and the triggering - // of this scheduled task. both want to acquire the lock - self.modifyStateAndRunActions { stateMachine in - if self._idleTimer.removeValue(forKey: connectionID) != nil { - // The timer still exists. State Machines assumes it is alive - return stateMachine.connectionIdleTimeout(connectionID) - } - return .none - } - } - - assert(self._idleTimer[connectionID] == nil) - self._idleTimer[connectionID] = scheduled - } - - private func cancelIdleTimerForConnection(_ connectionID: Connection.ID) { - self.logger.trace( - "Cancel idle connection timeout timer", - metadata: [ - "ahc-connection-id": "\(connectionID)" - ] - ) - guard let cancelTimer = self._idleTimer.removeValue(forKey: connectionID) else { - preconditionFailure("Expected to have an idle timer for connection \(connectionID) at this point.") - } - cancelTimer.cancel() - } - - private func scheduleConnectionStartBackoffTimer( - _ connectionID: Connection.ID, - _ timeAmount: TimeAmount, - on eventLoop: EventLoop - ) { - self.logger.trace( - "Schedule connection creation backoff timer", - metadata: [ - "ahc-connection-id": "\(connectionID)" - ] - ) - - let scheduled = eventLoop.scheduleTask(in: timeAmount) { - // there might be a race between a backoffTimer and the pool shutting down. - self.modifyStateAndRunActions { stateMachine in - if self._backoffTimer.removeValue(forKey: connectionID) != nil { - // The timer still exists. State Machines assumes it is alive - return stateMachine.connectionCreationBackoffDone(connectionID) - } - return .none - } - } - - assert(self._backoffTimer[connectionID] == nil) - self._backoffTimer[connectionID] = scheduled - } - - private func cancelConnectionStartBackoffTimer(_ connectionID: Connection.ID) { - guard let backoffTimer = self._backoffTimer.removeValue(forKey: connectionID) else { - preconditionFailure("Expected to have a backoff timer for connection \(connectionID) at this point.") - } - backoffTimer.cancel() - } -} - -// MARK: - Protocol methods - - -extension HTTPConnectionPool: HTTPConnectionRequester { - func http1ConnectionCreated(_ connection: HTTP1Connection.SendableView) { - self.logger.trace( - "successfully created connection", - metadata: [ - "ahc-connection-id": "\(connection.id)", - "ahc-http-version": "http/1.1", - ] - ) - self.modifyStateAndRunActions { - $0.newHTTP1ConnectionCreated(.http1_1(connection)) - } - } - - func http2ConnectionCreated(_ connection: HTTP2Connection.SendableView, maximumStreams: Int) { - self.logger.trace( - "successfully created connection", - metadata: [ - "ahc-connection-id": "\(connection.id)", - "ahc-http-version": "http/2", - "ahc-max-streams": "\(maximumStreams)", - ] - ) - self.modifyStateAndRunActions { - $0.newHTTP2ConnectionCreated(.http2(connection), maxConcurrentStreams: maximumStreams) - } - } - - func failedToCreateHTTPConnection(_ connectionID: HTTPConnectionPool.Connection.ID, error: Error) { - self.logger.debug( - "connection attempt failed", - metadata: [ - "ahc-error": "\(error)", - "ahc-connection-id": "\(connectionID)", - ] - ) - self.modifyStateAndRunActions { - $0.failedToCreateNewConnection(error, connectionID: connectionID) - } - } - - func waitingForConnectivity(_ connectionID: HTTPConnectionPool.Connection.ID, error: Error) { - self.logger.debug( - "waiting for connectivity", - metadata: [ - "ahc-error": "\(error)", - "ahc-connection-id": "\(connectionID)", - ] - ) - self.modifyStateAndRunActions { - $0.waitingForConnectivity(error, connectionID: connectionID) - } - } -} - -extension HTTPConnectionPool: HTTP1ConnectionDelegate { - func http1ConnectionClosed(_ id: HTTPConnectionPool.Connection.ID) { - self.logger.debug( - "connection closed", - metadata: [ - "ahc-connection-id": "\(id)", - "ahc-http-version": "http/1.1", - ] - ) - self.modifyStateAndRunActions { - $0.http1ConnectionClosed(id) - } - } - - func http1ConnectionReleased(_ id: HTTPConnectionPool.Connection.ID) { - self.logger.trace( - "releasing connection", - metadata: [ - "ahc-connection-id": "\(id)", - "ahc-http-version": "http/1.1", - ] - ) - self.modifyStateAndRunActions { - $0.http1ConnectionReleased(id) - } - } -} - -extension HTTPConnectionPool: HTTP2ConnectionDelegate { - func http2Connection(_ id: HTTPConnectionPool.Connection.ID, newMaxStreamSetting: Int) { - self.logger.debug( - "new max stream setting", - metadata: [ - "ahc-connection-id": "\(id)", - "ahc-http-version": "http/2", - "ahc-max-streams": "\(newMaxStreamSetting)", - ] - ) - self.modifyStateAndRunActions { - $0.newHTTP2MaxConcurrentStreamsReceived(id, newMaxStreams: newMaxStreamSetting) - } - } - - func http2ConnectionGoAwayReceived(_ id: HTTPConnectionPool.Connection.ID) { - self.logger.debug( - "connection go away received", - metadata: [ - "ahc-connection-id": "\(id)", - "ahc-http-version": "http/2", - ] - ) - self.modifyStateAndRunActions { - $0.http2ConnectionGoAwayReceived(id) - } - } - - func http2ConnectionClosed(_ id: HTTPConnectionPool.Connection.ID) { - self.logger.debug( - "connection closed", - metadata: [ - "ahc-connection-id": "\(id)", - "ahc-http-version": "http/2", - ] - ) - self.modifyStateAndRunActions { - $0.http2ConnectionClosed(id) - } - } - - func http2ConnectionStreamClosed(_ id: HTTPConnectionPool.Connection.ID, availableStreams: Int) { - self.logger.trace( - "stream closed", - metadata: [ - "ahc-connection-id": "\(id)", - "ahc-http-version": "http/2", - ] - ) - self.modifyStateAndRunActions { - $0.http2ConnectionStreamClosed(id) - } - } -} - -extension HTTPConnectionPool: HTTPRequestScheduler { - func cancelRequest(_ request: HTTPSchedulableRequest) { - let requestID = Request(request).id - self.modifyStateAndRunActions { - $0.cancelRequest(requestID) - } - } -} - -extension HTTPConnectionPool { - struct Connection: Hashable { - typealias ID = Int - - private enum Reference { - case http1_1(HTTP1Connection.SendableView) - case http2(HTTP2Connection.SendableView) - case __testOnly_connection(ID, EventLoop) - } - - private let _ref: Reference - - fileprivate static func http1_1(_ conn: HTTP1Connection.SendableView) -> Self { - Connection(_ref: .http1_1(conn)) - } - - fileprivate static func http2(_ conn: HTTP2Connection.SendableView) -> Self { - Connection(_ref: .http2(conn)) - } - - static func __testOnly_connection(id: ID, eventLoop: EventLoop) -> Self { - Connection(_ref: .__testOnly_connection(id, eventLoop)) - } - - var id: ID { - switch self._ref { - case .http1_1(let connection): - return connection.id - case .http2(let connection): - return connection.id - case .__testOnly_connection(let id, _): - return id - } - } - - var eventLoop: EventLoop { - switch self._ref { - case .http1_1(let connection): - return connection.channel.eventLoop - case .http2(let connection): - return connection.channel.eventLoop - case .__testOnly_connection(_, let eventLoop): - return eventLoop - } - } - - fileprivate func executeRequest(_ request: HTTPExecutableRequest) { - switch self._ref { - case .http1_1(let connection): - return connection.executeRequest(request) - case .http2(let connection): - return connection.executeRequest(request) - case .__testOnly_connection: - break - } - } - - /// Shutdown cancels any running requests on the connection and then closes the connection - fileprivate func shutdown() { - switch self._ref { - case .http1_1(let connection): - return connection.shutdown() - case .http2(let connection): - return connection.shutdown() - case .__testOnly_connection: - break - } - } - - /// Closes the connection without cancelling running requests. Use this when you are sure, that the - /// connection is currently idle. - fileprivate func close(promise: EventLoopPromise?) { - switch self._ref { - case .http1_1(let connection): - return connection.close(promise: promise) - case .http2(let connection): - return connection.close(promise: promise) - case .__testOnly_connection: - promise?.succeed(()) - } - } - - static func == (lhs: HTTPConnectionPool.Connection, rhs: HTTPConnectionPool.Connection) -> Bool { - switch (lhs._ref, rhs._ref) { - case (.http1_1(let lhsConn), .http1_1(let rhsConn)): - return lhsConn.id == rhsConn.id - case (.http2(let lhsConn), .http2(let rhsConn)): - return lhsConn.id == rhsConn.id - case ( - .__testOnly_connection(let lhsID, let lhsEventLoop), .__testOnly_connection(let rhsID, let rhsEventLoop) - ): - return lhsID == rhsID && lhsEventLoop === rhsEventLoop - default: - return false - } - } - - func hash(into hasher: inout Hasher) { - switch self._ref { - case .http1_1(let conn): - hasher.combine(conn.id) - case .http2(let conn): - hasher.combine(conn.id) - case .__testOnly_connection(let id, let eventLoop): - hasher.combine(id) - hasher.combine(eventLoop.id) - } - } - } -} - -extension HTTPConnectionPool { - /// This is a wrapper that we use inside the connection pool state machine to ensure that - /// the actual request can not be accessed at any time. Further it exposes all that is needed within - /// the state machine. A request ID and the `EventLoop` requirement. - struct Request { - struct ID: Hashable { - let objectIdentifier: ObjectIdentifier - let eventLoopID: EventLoopID? - - fileprivate init(_ request: HTTPSchedulableRequest, eventLoopRequirement eventLoopID: EventLoopID?) { - self.objectIdentifier = ObjectIdentifier(request) - self.eventLoopID = eventLoopID - } - } - - fileprivate let req: HTTPSchedulableRequest - - init(_ request: HTTPSchedulableRequest) { - self.req = request - } - - var id: HTTPConnectionPool.Request.ID { - HTTPConnectionPool.Request.ID(self.req, eventLoopRequirement: self.requiredEventLoop?.id) - } - - var requiredEventLoop: EventLoop? { - self.req.requiredEventLoop - } - - var preferredEventLoop: EventLoop { - self.req.preferredEventLoop - } - - var connectionDeadline: NIODeadline { - self.req.connectionDeadline - } - - func __testOnly_wrapped_request() -> HTTPSchedulableRequest { - self.req - } - } -} - -struct EventLoopID: Hashable { - private var id: Identifier - - private enum Identifier: Hashable { - case objectIdentifier(ObjectIdentifier) - case __testOnly_fakeID(Int) - } - - init(_ eventLoop: EventLoop) { - self.init(.objectIdentifier(ObjectIdentifier(eventLoop))) - } - - private init(_ id: Identifier) { - self.id = id - } - - static func __testOnly_fakeID(_ id: Int) -> EventLoopID { - EventLoopID(.__testOnly_fakeID(id)) - } -} - -extension EventLoop { - var id: EventLoopID { EventLoopID(self) } -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/HTTPExecutableRequest.swift b/Sources/AsyncHTTPClient/ConnectionPool/HTTPExecutableRequest.swift deleted file mode 100644 index bce55eb5b..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/HTTPExecutableRequest.swift +++ /dev/null @@ -1,270 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOCore -import NIOHTTP1 -import NIOSSL - -/// # Protocol Overview -/// -/// To support different public request APIs we abstract the actual request implementations behind -/// protocols. During the lifetime of a request, a request must conform to different protocols -/// depending on which state it is in. -/// -/// Generally there are two main states in a request's lifetime: -/// -/// 1. **The request is scheduled to be run.** -/// In this state the HTTP client tries to acquire a connection for the request, and the request -/// may need to wait for a connection -/// 2. **The request is executing.** -/// In this state the request was written to a NIO channel. A NIO channel handler (abstracted -/// by the `HTTPRequestExecutor` protocol) writes the request's bytes onto the wire and -/// dispatches the http response bytes back to the response. -/// -/// -/// ## Request is scheduled -/// -/// When the `HTTPClient` shall send an HTTP request, it will use its `HTTPConnectionPool.Manager` to -/// determine the `HTTPConnectionPool` to run the request on. After a `HTTPConnectionPool` has been -/// found for the request, the request will be executed on this connection pool. Since the HTTP -/// request implements the `HTTPSchedulableRequest` protocol, the HTTP connection pool can communicate -/// with the request. The `HTTPConnectionPool` implements the `HTTPRequestScheduler` protocol. -/// -/// 1. The `HTTPConnectionPool` tries to find an idle connection for the request based on its -/// `eventLoopPreference`. -/// -/// 2. If an idle connection is available to the request, the request will be passed to the -/// connection right away. In this case the `HTTPConnectionPool` will only use the -/// `HTTPSchedulableRequest`'s `eventLoopPreference` property. No other methods will be called. -/// -/// 3. If no idle connection is available to the request, the request will be queued for execution: -/// - The `HTTPConnectionPool` will inform the request that it is queued for execution by -/// calling: `requestWasQueued(_: HTTPRequestScheduler)`. The request must store a reference -/// to the `HTTPRequestScheduler`. The request must call `cancelRequest(self)` on the -/// scheduler, if the request was cancelled, while waiting for execution. -/// - The `HTTPConnectionPool` will create a connection deadline based on the -/// `HTTPSchedulableRequest`'s `connectionDeadline` property. If a connection to execute the -/// request on, was not found before this deadline the request will be failed. -/// - The HTTPConnectionPool will call `fail(_: Error)` on the `HTTPSchedulableRequest` to -/// inform the request about having overrun the `connectionDeadline`. -/// -/// -/// ## Request is executing -/// -/// After the `HTTPConnectionPool` has identified a connection for the request to be run on, it will -/// execute the request on this connection. (Implementation detail: This happens by writing the -/// `HTTPExecutableRequest` to a `NIO.Channel`. We expect the last handler in the `ChannelPipeline` -/// to have an `OutboundIn` type of `HTTPExecutableRequest`. Further we expect that the handler -/// also conforms to the protocol `HTTPRequestExecutor` to allow communication of the request with -/// the executor/`ChannelHandler`). -/// -/// The request execution will work as follows: -/// -/// 1. The request executor will call `willExecuteRequest(_: HTTPRequestExecutor)` on the -/// request. The request is expected to keep a reference to the `HTTPRequestExecutor` that was -/// passed to the request for further communication. -/// 2. The request sending is started by the executor accessing the `HTTPExecutableRequest`'s -/// property `requestHead: HTTPRequestHead`. Based on the `requestHead` the executor can -/// determine if the request has a body (Is a "content-length" or "transfer-encoding" -/// header present?). -/// 3. The executor will write the request's header into the Channel. If no body is present, the -/// executor will also write a request end into the Channel. After this the executor will call -/// `requestHeadSent(_: HTTPRequestHead)` -/// 4. If the request has a body the request executor will, ask the request for body data, by -/// calling `startRequestBodyStream()`. The request is expected to call -/// `writeRequestBodyPart(_: IOData, task: HTTPExecutableRequest)` on the executor with body -/// data. -/// - The executor can signal backpressure to the request by calling -/// `pauseRequestBodyStream()`. In this case the request is expected to stop calling -/// `writeRequestBodyPart(_: IOData, task: HTTPExecutableRequest)`. However because of race -/// conditions the executor is prepared to process more data, even though it asked the -/// request to pause. -/// - Once the executor is able to send more data, it will notify the request by calling -/// `resumeRequestBodyStream()` on the request. -/// - The request shall call `finishRequestBodyStream()` on the executor to signal that the -/// request body was sent. -/// 5. Once the executor receives a http response from the Channel, it will forward the http -/// response head to the `HTTPExecutableRequest` by calling `receiveResponseHead` on it. -/// - The executor will forward all the response body parts it receives in a single read to -/// the `HTTPExecutableRequest` without any buffering by calling -/// `receiveResponseBodyPart(_ buffer: ByteBuffer)` right away. It is the task's job to -/// buffer the responses for user consumption. -/// - Once the executor has finished a read, it will not schedule another read, until the -/// request calls `demandResponseBodyStream(task: HTTPExecutableRequest)` on the executor. -/// - Once the executor has received the response's end, it will forward this message by -/// calling `receiveResponseEnd()` on the `HTTPExecutableRequest`. -/// 6. If a channel error occurs during the execution of the request, or if the channel becomes -/// inactive the executor will notify the request by calling `fail(_ error: Error)` on it. -/// 7. If the request is cancelled, while it is executing on the executor, it must call -/// `cancelRequest(task: HTTPExecutableRequest)` on the executor. -/// -/// -/// ## Further notes -/// -/// - These protocols makes no guarantees about thread safety at all. It is implementations job to -/// ensure thread safety. -/// - However all calls to the `HTTPRequestScheduler` and `HTTPRequestExecutor` require that the -/// invoking request is passed along. This helps the scheduler and executor in race conditions. -/// Example: -/// - The executor may have received an error in thread A that it passes along to the request. -/// After having passed on the error, the executor considers the request done and releases -/// the request's reference. -/// - The request may issue a call to `writeRequestBodyPart(_: IOData, task: HTTPExecutableRequest)` -/// on thread B in the same moment the request error above occurred. For this reason it may -/// happen that the executor receives, the invocation of `writeRequestBodyPart` after it has -/// failed the request. -/// Passing along the requests reference helps the executor and scheduler verify its internal -/// state. - -/// A handle to the request scheduler. -/// -/// Use this handle to cancel the request, while it is waiting for a free connection, to execute the request. -/// This protocol is only intended to be implemented by the `HTTPConnectionPool`. -protocol HTTPRequestScheduler: Sendable { - /// Informs the task queuer that a request has been cancelled. - func cancelRequest(_: HTTPSchedulableRequest) -} - -/// An abstraction over a request that we want to send. A request may need to communicate with its request -/// queuer and executor. The client's methods will be called synchronously on an `EventLoop` by the -/// executor. For this reason it is very important that the implementation of these functions never blocks. -protocol HTTPSchedulableRequest: HTTPExecutableRequest { - /// The tasks connection pool key - /// - /// Based on this key the correct connection pool will be chosen for the request - var poolKey: ConnectionPool.Key { get } - - /// An optional custom `TLSConfiguration`. - /// - /// If you want to override the default `TLSConfiguration` ensure that this property is non nil - var tlsConfiguration: TLSConfiguration? { get } - - /// The task's logger - var logger: Logger { get } - - /// A connection to run this task on needs to be found before this deadline! - var connectionDeadline: NIODeadline { get } - - /// The user has expressed an intent for this request to be executed on this EventLoop. If a - /// connection is available on another one, just use the one handy. - var preferredEventLoop: EventLoop { get } - - /// The user required the request to be executed on a connection that is handled by this EventLoop. - var requiredEventLoop: EventLoop? { get } - - /// Informs the task, that it was queued for execution - /// - /// This happens if all available connections are currently in use - func requestWasQueued(_: HTTPRequestScheduler) - - /// Fails the queued request, with an error. - func fail(_ error: Error) -} - -/// A handle to the request executor. -/// -/// This protocol is implemented by the `HTTP1ClientChannelHandler`. -protocol HTTPRequestExecutor: Sendable { - /// Writes a body part into the channel pipeline - /// - /// This method may be **called on any thread**. The executor needs to ensure thread safety. - func writeRequestBodyPart(_: IOData, request: HTTPExecutableRequest, promise: EventLoopPromise?) - - /// Signals that the request body stream has finished - /// - /// This method may be **called on any thread**. The executor needs to ensure thread safety. - func finishRequestBodyStream(_ task: HTTPExecutableRequest, promise: EventLoopPromise?) - - /// Signals that more bytes from response body stream can be consumed. - /// - /// The request executor will call `receiveResponseBodyPart(_ buffer: ByteBuffer)` with more data after - /// this call. - /// - /// This method may be **called on any thread**. The executor needs to ensure thread safety. - func demandResponseBodyStream(_ task: HTTPExecutableRequest) - - /// Signals that the request has been cancelled. - /// - /// This method may be **called on any thread**. The executor needs to ensure thread safety. - func cancelRequest(_ task: HTTPExecutableRequest) -} - -protocol HTTPExecutableRequest: AnyObject, Sendable { - /// The request's logger - var logger: Logger { get } - - /// The request's head. - /// - /// The HTTP request head, that shall be sent. The HTTPRequestExecutor **will not** run any validation - /// check on the request head. All necessary metadata about the request head the executor expects in - /// the ``requestFramingMetadata``. - var requestHead: HTTPRequestHead { get } - - /// The request's framing metadata. - /// - /// The request framing metadata that is derived from the ``requestHead``. Based on the content of the - /// request framing metadata the executor will call ``startRequestBodyStream`` after - /// ``requestHeadSent``. - var requestFramingMetadata: RequestFramingMetadata { get } - - /// Request specific configurations - var requestOptions: RequestOptions { get } - - /// Will be called by the ChannelHandler to indicate that the request is going to be sent. - /// - /// This will be called on the Channel's EventLoop. Do **not block** during your execution! If the - /// request is cancelled after the `willExecuteRequest` method was called. The executing - /// request must call `executor.cancel()` to stop request execution. - func willExecuteRequest(_: HTTPRequestExecutor) - - /// Will be called by the ChannelHandler to indicate that the request head has been sent. - /// - /// This will be called on the Channel's EventLoop. Do **not block** during your execution! - func requestHeadSent() - - /// Start or resume request body streaming - /// - /// This will be called on the Channel's EventLoop. Do **not block** during your execution! - func resumeRequestBodyStream() - - /// Pause request streaming - /// - /// This will be called on the Channel's EventLoop. Do **not block** during your execution! - func pauseRequestBodyStream() - - /// Receive a response head. - /// - /// Please note that `receiveResponseHead` and `receiveResponseBodyPart` may - /// be called in quick succession. It is the task's job to buffer those events for the user. Once all - /// buffered data has been consumed the task must call `executor.demandResponseBodyStream` - /// to ask for more data. - func receiveResponseHead(_ head: HTTPResponseHead) - - /// Receive response body stream parts. - /// - /// Please note that `receiveResponseHead` and `receiveResponseBodyPart` may - /// be called in quick succession. It is the task's job to buffer those events for the user. Once all - /// buffered data has been consumed the task must call `executor.demandResponseBodyStream` - /// to ask for more data. - func receiveResponseBodyParts(_ buffer: CircularBuffer) - - /// Succeeds the executing request. The executor will not call any further methods on the request after this method. - /// - /// - Parameter buffer: The remaining response body parts, that were received before the request end - func succeedRequest(_ buffer: CircularBuffer?) - - /// Fails the executing request, with an error. - func fail(_ error: Error) -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/HTTPRequestStateMachine+Demand.swift b/Sources/AsyncHTTPClient/ConnectionPool/HTTPRequestStateMachine+Demand.swift deleted file mode 100644 index 5c5b893e0..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/HTTPRequestStateMachine+Demand.swift +++ /dev/null @@ -1,192 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore - -extension HTTPRequestStateMachine { - /// A sub state for receiving a response events. Stores whether the consumer has either signaled demand and whether the - /// channel has issued `read` events. - struct ResponseStreamState { - private enum State { - /// The state machines expects further writes to `channelRead`. The writes are appended to the buffer. - case waitingForBytes(CircularBuffer) - /// The state machines expects a call to `demandMoreResponseBodyParts` or `read`. The buffer is - /// empty. It is preserved for performance reasons. - case waitingForReadOrDemand(CircularBuffer) - /// The state machines expects a call to `read`. The buffer is empty. It is preserved for performance reasons. - case waitingForRead(CircularBuffer) - /// The state machines expects a call to `demandMoreResponseBodyParts`. The buffer is empty. It is - /// preserved for performance reasons. - case waitingForDemand(CircularBuffer) - - case modifying - } - - enum Action { - case read - case wait - } - - private var state: State - - init() { - self.state = .waitingForBytes(CircularBuffer(initialCapacity: 16)) - } - - mutating func receivedBodyPart(_ body: ByteBuffer) { - switch self.state { - case .waitingForBytes(var buffer): - self.state = .modifying - buffer.append(body) - self.state = .waitingForBytes(buffer) - - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - - // For all the following cases, please note: - // Normally these code paths should never be hit. However there is one way to trigger - // this: - // - // If the server decides to close a connection, NIO will forward all outstanding - // `channelRead`s without waiting for a next `context.read` call. For this reason we - // might receive further bytes, when we don't expect them here. - - case .waitingForRead(var buffer): - self.state = .modifying - buffer.append(body) - self.state = .waitingForRead(buffer) - - case .waitingForDemand(var buffer): - self.state = .modifying - buffer.append(body) - self.state = .waitingForDemand(buffer) - - case .waitingForReadOrDemand(var buffer): - self.state = .modifying - buffer.append(body) - self.state = .waitingForReadOrDemand(buffer) - } - } - - mutating func channelReadComplete() -> CircularBuffer? { - switch self.state { - case .waitingForBytes(let buffer): - if buffer.isEmpty { - self.state = .waitingForRead(buffer) - return nil - } else { - var newBuffer = buffer - newBuffer.removeAll(keepingCapacity: true) - self.state = .waitingForReadOrDemand(newBuffer) - return buffer - } - - // For all the following cases, please note: - // Normally these code paths should never be hit. However there is one way to trigger - // this: - // - // If the connection to a server is closed, NIO will forward all outstanding - // `channelRead`s without waiting for a next `context.read` call. After all - // `channelRead`s are delivered, we will also see a `channelReadComplete` call. After - // this has happened, we know that we will get a channelInactive or further - // `channelReads`. If the request ever gets to an `.end` all buffered data will be - // forwarded to the user. - - case .waitingForRead, - .waitingForDemand, - .waitingForReadOrDemand: - return nil - - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - mutating func demandMoreResponseBodyParts() -> Action { - switch self.state { - case .waitingForDemand(let buffer): - self.state = .waitingForBytes(buffer) - return .read - - case .waitingForReadOrDemand(let buffer): - self.state = .waitingForRead(buffer) - return .wait - - case .waitingForRead: - // if we are `waitingForRead`, no action needs to be taken. Demand was already signalled - // once we receive the next `read`, we will forward it, right away - return .wait - - case .waitingForBytes: - // if we are `.waitingForBytes`, no action needs to be taken. As soon as we receive - // the next channelReadComplete we will forward all buffered data - return .wait - - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - mutating func read() -> Action { - switch self.state { - case .waitingForBytes: - // This should never happen. But we don't want to precondition this behavior. Let's just - // pass the read event on - return .read - - case .waitingForReadOrDemand(let buffer): - self.state = .waitingForDemand(buffer) - return .wait - - case .waitingForRead(let buffer): - self.state = .waitingForBytes(buffer) - return .read - - case .waitingForDemand: - // we have already received a read event. We will issue it as soon as we received demand - // from the consumer - return .wait - - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - enum ConnectionAction { - case none - case close - } - - mutating func end() -> (CircularBuffer, ConnectionAction) { - switch self.state { - case .waitingForBytes(let buffer): - return (buffer, .none) - - case .waitingForReadOrDemand(let buffer), - .waitingForRead(let buffer), - .waitingForDemand(let buffer): - // Normally this code path should never be hit. However there is one way to trigger - // this: - // - // If the server decides to close a connection, NIO will forward all outstanding - // `channelRead`s without waiting for a next `context.read` call. For this reason we - // might receive a call to `end()`, when we don't expect it here. - return (buffer, .close) - - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - } -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/HTTPRequestStateMachine.swift b/Sources/AsyncHTTPClient/ConnectionPool/HTTPRequestStateMachine.swift deleted file mode 100644 index e06389360..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/HTTPRequestStateMachine.swift +++ /dev/null @@ -1,923 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOHTTP1 -import NIOSSL - -struct HTTPRequestStateMachine { - fileprivate enum State { - /// The initial state machine state. The only valid mutation is `start()`. The state will - /// transitions to: - /// - `.waitForChannelToBecomeWritable` (if the channel becomes non writable while sending the header) - /// - `.sendingHead` if the channel is writable - case initialized - - /// Waiting for the channel to be writable. Valid transitions are: - /// - `.running(.streaming, .waitingForHead)` (once the Channel is writable again and if a request body is expected) - /// - `.running(.endSent, .waitingForHead)` (once the Channel is writable again and no request body is expected) - /// - `.failed` (if a connection error occurred) - case waitForChannelToBecomeWritable(HTTPRequestHead, RequestFramingMetadata) - - /// A request is on the wire. Valid transitions are: - /// - `.finished` - /// - `.failed` - case running(RequestState, ResponseState) - - /// The request has completed successfully - case finished - - /// The request has failed - case failed(Error) - - case modifying - } - - /// A sub state for a running request. More specifically for sending a request body. - fileprivate enum RequestState { - /// A sub state for sending a request body. Stores whether a producer should produce more - /// bytes or should pause. - enum ProducerControlState: String { - /// The request body producer should produce more body bytes. The channel is writable. - case producing - /// The request body producer should pause producing more bytes. The channel is not writable. - case paused - } - - /// The request is streaming its request body. `expectedBodyLength` has a value, if the request header contained - /// a `"content-length"` header field. If the request header contained a `"transfer-encoding" = "chunked"` - /// header field, the `expectedBodyLength` is `nil`. - case streaming(expectedBodyLength: Int64?, sentBodyBytes: Int64, producer: ProducerControlState) - /// The request has sent its request body and end. - case endSent - } - - fileprivate enum ResponseState { - /// A response head has not been received yet. - case waitingForHead - /// A response head has been received and we are ready to consume more data off the wire - case receivingBody(HTTPResponseHead, ResponseStreamState) - /// A response end has been received. We don't expect more bytes from the wire. - case endReceived - } - - enum Action { - /// A action to execute, when we consider a successful request "done". - enum FinalSuccessfulRequestAction { - /// Close the connection - case close - /// If the server has replied, with a status of 200...300 before all data was sent, a request is considered succeeded, - /// as soon as we wrote the request end onto the wire. - /// - /// The promise is an optional write promise. - case sendRequestEnd(EventLoopPromise?) - /// Do nothing. This is action is used, if the request failed, before we the request head was written onto the wire. - /// This might happen if the request is cancelled, or the request failed the soundness check. - case none - } - - /// A action to execute, when we consider a failed request "done". - enum FinalFailedRequestAction { - /// Close the connection - case close(EventLoopPromise?) - /// Do nothing. This is action is used, if the request failed, before we the request head was written onto the wire. - /// This might happen if the request is cancelled, or the request failed the soundness check. - case none - } - - case sendRequestHead(HTTPRequestHead, sendEnd: Bool) - case notifyRequestHeadSendSuccessfully( - resumeRequestBodyStream: Bool, - startIdleTimer: Bool - ) - case sendBodyPart(IOData, EventLoopPromise?) - case sendRequestEnd(EventLoopPromise?) - case failSendBodyPart(Error, EventLoopPromise?) - case failSendStreamFinished(Error, EventLoopPromise?) - - case pauseRequestBodyStream - case resumeRequestBodyStream - - case forwardResponseHead(HTTPResponseHead, pauseRequestBodyStream: Bool) - case forwardResponseBodyParts(CircularBuffer) - - case failRequest(Error, FinalFailedRequestAction) - case succeedRequest(FinalSuccessfulRequestAction, CircularBuffer) - - case read - case wait - } - - private var state: State = .initialized - - private var isChannelWritable: Bool - - init(isChannelWritable: Bool) { - self.isChannelWritable = isChannelWritable - } - - mutating func startRequest(head: HTTPRequestHead, metadata: RequestFramingMetadata) -> Action { - switch self.state { - case .initialized: - guard self.isChannelWritable else { - self.state = .waitForChannelToBecomeWritable(head, metadata) - return .wait - } - return self.startSendingRequest(head: head, metadata: metadata) - - case .failed: - // The request state machine is marked as failed before the request is started, if - // the request was cancelled before hitting the channel handler. Before `startRequest` - // is called on the state machine, `willExecuteRequest` is called on - // `HTTPExecutableRequest`, which might loopback to state machines cancel method. - return .wait - - case .running, .finished, .waitForChannelToBecomeWritable, .modifying: - preconditionFailure("`startRequest()` must be called first, and exactly once. Invalid state: \(self.state)") - } - } - - mutating func writabilityChanged(writable: Bool) -> Action { - if writable { - return self.channelIsWritable() - } else { - return self.channelIsNotWritable() - } - } - - private mutating func channelIsWritable() -> Action { - self.isChannelWritable = true - - switch self.state { - case .initialized, - .running(.streaming(_, _, producer: .producing), _), - .running(.endSent, _), - .finished, - .failed: - return .wait - - case .waitForChannelToBecomeWritable(let head, let metadata): - return self.startSendingRequest(head: head, metadata: metadata) - - case .running(.streaming(_, _, producer: .paused), .receivingBody(let head, _)) where head.status.code >= 300: - // If we are receiving a response with a status of >= 300, we should not send out - // further request body parts. The remote already signaled with status >= 300 that it - // won't be interested. Let's save some bandwidth. - return .wait - - case .running(.streaming(let expectedBody, let sentBodyBytes, producer: .paused), let responseState): - let requestState: RequestState = .streaming( - expectedBodyLength: expectedBody, - sentBodyBytes: sentBodyBytes, - producer: .producing - ) - - self.state = .running(requestState, responseState) - return .resumeRequestBodyStream - - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - private mutating func channelIsNotWritable() -> Action { - self.isChannelWritable = false - - switch self.state { - case .initialized, - .waitForChannelToBecomeWritable, - .running(.streaming(_, _, producer: .paused), _), - .running(.endSent, _), - .finished, - .failed: - return .wait - - case .running(.streaming(let expectedBodyLength, let sentBodyBytes, producer: .producing), let responseState): - let requestState: RequestState = .streaming( - expectedBodyLength: expectedBodyLength, - sentBodyBytes: sentBodyBytes, - producer: .paused - ) - self.state = .running(requestState, responseState) - return .pauseRequestBodyStream - - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - mutating func errorHappened(_ error: Error) -> Action { - if let error = error as? NIOSSLError, - error == .uncleanShutdown, - let action = self.handleNIOSSLUncleanShutdownError() - { - return action - } - switch self.state { - case .initialized: - preconditionFailure( - "After the state machine has been initialized, start must be called immediately. Thus this state is unreachable" - ) - case .waitForChannelToBecomeWritable: - // the request failed, before it was sent onto the wire. - self.state = .failed(error) - return .failRequest(error, .none) - - case .running: - self.state = .failed(error) - return .failRequest(error, .close(nil)) - - case .finished, .failed: - // ignore error - return .wait - - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - private mutating func handleNIOSSLUncleanShutdownError() -> Action? { - switch self.state { - case .running(.streaming, .waitingForHead), - .running(.endSent, .waitingForHead): - // if we received a NIOSSL.uncleanShutdown before we got an answer we should handle - // this like a normal connection close. We will receive a call to channelInactive after - // this error. - return .wait - - case .running(.streaming, .receivingBody(let responseHead, _)), - .running(.endSent, .receivingBody(let responseHead, _)): - // This code is only reachable for request and responses, which we expect to have a body. - // We depend on logic from the HTTPResponseDecoder here. The decoder will emit an - // HTTPResponsePart.end right after the HTTPResponsePart.head, for every request with a - // CONNECT or HEAD method and every response with a 1xx, 204 or 304 response status. - // - // For this reason we only need to check the "content-length" or "transfer-encoding" - // headers here to determine if we are potentially in an EOF terminated response. - - if responseHead.headers.contains(name: "content-length") - || responseHead.headers.contains(name: "transfer-encoding") - { - // If we have already received the response head, the parser will ensure that we - // receive a complete response, if the content-length or transfer-encoding header - // was set. In this case we can ignore the NIOSSLError.uncleanShutdown. We will see - // a HTTPParserError very soon. - return .wait - } - - // If the response is EOF terminated, we need to rely on a clean tls shutdown to be sure - // we have received all necessary bytes. For this reason we forward the uncleanShutdown - // error to the user. - self.state = .failed(NIOSSLError.uncleanShutdown) - return .failRequest(NIOSSLError.uncleanShutdown, .close(nil)) - - case .waitForChannelToBecomeWritable, .running, .finished, .failed, .initialized, .modifying: - return nil - } - } - - mutating func requestStreamPartReceived(_ part: IOData, promise: EventLoopPromise?) -> Action { - switch self.state { - case .initialized, - .waitForChannelToBecomeWritable, - .running(.endSent, _): - preconditionFailure( - "We must be in the request streaming phase, if we receive further body parts. Invalid state: \(self.state)" - ) - - case .running(.streaming(_, _, let producerState), .receivingBody(let head, _)) where head.status.code >= 300: - // If we have already received a response head with status >= 300, we won't send out any - // further request body bytes. Since the remote signaled with status >= 300, that it - // won't be interested. We expect that the producer has been informed to pause - // producing. - assert(producerState == .paused) - return .failSendBodyPart(HTTPClientError.requestStreamCancelled, promise) - - case .running(.streaming(let expectedBodyLength, var sentBodyBytes, let producerState), let responseState): - // We don't check the producer state here: - // - // No matter if the `producerState` is either `.producing` or `.paused` any bytes we - // receive shall be forwarded to the Channel right away. As long as we have not received - // a response with status >= 300. - // - // More streamed data is accepted, even though the producer may have been asked to - // pause. The reason for this is as follows: There might be thread synchronization - // situations in which the producer might not have received the plea to pause yet. - - if let expected = expectedBodyLength, sentBodyBytes + Int64(part.readableBytes) > expected { - let error = HTTPClientError.bodyLengthMismatch - self.state = .failed(error) - return .failRequest(error, .close(promise)) - } - - sentBodyBytes += Int64(part.readableBytes) - - let requestState: RequestState = .streaming( - expectedBodyLength: expectedBodyLength, - sentBodyBytes: sentBodyBytes, - producer: producerState - ) - - self.state = .running(requestState, responseState) - - return .sendBodyPart(part, promise) - - case .failed(let error): - return .failSendBodyPart(error, promise) - - case .finished: - // A request may be finished, before we have send all parts. This might be the case if - // the server responded with an HTTP status code that is equal or larger to 300 - // (Redirection, Client Error or Server Error). In those cases we pause the request body - // stream as soon as we have received the response head and we succeed the request as - // when response end is received. This may mean, that we succeed a request, even though - // we have not sent all it's body parts. - - // We may still receive something, here because of potential race conditions with the - // producing thread. - return .failSendBodyPart(HTTPClientError.requestStreamCancelled, promise) - - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - mutating func requestStreamFinished(promise: EventLoopPromise?) -> Action { - switch self.state { - case .initialized, - .waitForChannelToBecomeWritable, - .running(.endSent, _): - preconditionFailure( - "A request body stream end is only expected if we are in state request streaming. Invalid state: \(self.state)" - ) - - case .running(.streaming(let expectedBodyLength, let sentBodyBytes, _), .waitingForHead): - if let expected = expectedBodyLength, expected != sentBodyBytes { - let error = HTTPClientError.bodyLengthMismatch - self.state = .failed(error) - return .failRequest(error, .close(promise)) - } - - self.state = .running(.endSent, .waitingForHead) - return .sendRequestEnd(promise) - - case .running( - .streaming(let expectedBodyLength, let sentBodyBytes, _), - .receivingBody(let head, let streamState) - ): - assert(head.status.code < 300) - - if let expected = expectedBodyLength, expected != sentBodyBytes { - let error = HTTPClientError.bodyLengthMismatch - self.state = .failed(error) - return .failRequest(error, .close(promise)) - } - - self.state = .running(.endSent, .receivingBody(head, streamState)) - return .sendRequestEnd(promise) - - case .running(.streaming(let expectedBodyLength, let sentBodyBytes, _), .endReceived): - if let expected = expectedBodyLength, expected != sentBodyBytes { - let error = HTTPClientError.bodyLengthMismatch - self.state = .failed(error) - return .failRequest(error, .close(promise)) - } - - self.state = .finished - return .succeedRequest(.sendRequestEnd(promise), .init()) - - case .failed(let error): - return .failSendStreamFinished(error, promise) - - case .finished: - // A request may be finished, before we have send all parts. This might be the case if - // the server responded with an HTTP status code that is equal or larger to 300 - // (Redirection, Client Error or Server Error). In those cases we pause the request body - // stream as soon as we have received the response head and we succeed the request as - // when response end is received. This may mean, that we succeed a request, even though - // we have not sent all it's body parts. - - // We may still receive something, here because of potential race conditions with the - // producing thread. - return .failSendStreamFinished(HTTPClientError.requestStreamCancelled, promise) - - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - mutating func requestCancelled() -> Action { - switch self.state { - case .initialized, .waitForChannelToBecomeWritable: - let error = HTTPClientError.cancelled - self.state = .failed(error) - // Okay, this has different semantics for HTTP/1 and HTTP/2. In HTTP/1 we don't want to - // close the connection, if we haven't sent anything yet, to reuse the connection for - // another request. In HTTP/2 we must close the channel to ensure it is released from - // HTTP/2 multiplexer. - return .failRequest(error, .none) - - case .running: - let error = HTTPClientError.cancelled - self.state = .failed(error) - return .failRequest(error, .close(nil)) - - case .finished: - return .wait - - case .failed: - return .wait - - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - mutating func channelInactive() -> Action { - switch self.state { - case .initialized, .waitForChannelToBecomeWritable, .running: - let error = HTTPClientError.remoteConnectionClosed - self.state = .failed(error) - return .failRequest(error, .none) - - case .finished: - return .wait - - case .failed: - // don't overwrite error - return .wait - - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - // MARK: - Response - - mutating func read() -> Action { - switch self.state { - case .initialized, - .waitForChannelToBecomeWritable, - .running(_, .waitingForHead), - .running(_, .endReceived), - .finished, - .failed: - // If we are not in the middle of streaming the response body, we always want to get - // more data... - return .read - - case .running(let requestState, .receivingBody(let head, var streamState)): - // This should never happen. But we don't want to precondition this behavior. Let's just - // pass the read event on - return self.avoidingStateMachineCoW { state -> Action in - let action = streamState.read() - state = .running(requestState, .receivingBody(head, streamState)) - return action.toRequestAction() - } - - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - mutating func channelRead(_ part: HTTPClientResponsePart) -> Action { - switch part { - case .head(let head): - return self.receivedHTTPResponseHead(head) - case .body(let body): - return self.receivedHTTPResponseBodyPart(body) - case .end: - return self.receivedHTTPResponseEnd() - } - } - - mutating func channelReadComplete() -> Action { - switch self.state { - case .initialized, - .waitForChannelToBecomeWritable, - .running(_, .waitingForHead), - .running(_, .endReceived), - .finished, - .failed: - return .wait - - case .running(let requestState, .receivingBody(let head, var streamState)): - // This should never happen. But we don't want to precondition this behavior. Let's just - // pass the read event on - return self.avoidingStateMachineCoW { state -> Action in - let buffer = streamState.channelReadComplete() - state = .running(requestState, .receivingBody(head, streamState)) - if let buffer = buffer { - return .forwardResponseBodyParts(buffer) - } else { - return .wait - } - } - - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - private mutating func receivedHTTPResponseHead(_ head: HTTPResponseHead) -> Action { - guard head.status.code >= 200 || head.status == .switchingProtocols else { - // We ignore any leading 1xx headers except for 101 (switching protocols). The - // HTTP1ConnectionStateMachine ensures the connection close for 101 after the `.end` is - // received. - return .wait - } - - switch self.state { - case .initialized, .waitForChannelToBecomeWritable: - preconditionFailure( - "How can we receive a response head before sending a request head ourselves \(self.state)" - ) - - case .running(.streaming(let expectedBodyLength, let sentBodyBytes, producer: .paused), .waitingForHead): - self.state = .running( - .streaming(expectedBodyLength: expectedBodyLength, sentBodyBytes: sentBodyBytes, producer: .paused), - .receivingBody(head, .init()) - ) - return .forwardResponseHead(head, pauseRequestBodyStream: false) - - case .running(.streaming(let expectedBodyLength, let sentBodyBytes, producer: .producing), .waitingForHead): - if head.status.code >= 300 { - self.state = .running( - .streaming(expectedBodyLength: expectedBodyLength, sentBodyBytes: sentBodyBytes, producer: .paused), - .receivingBody(head, .init()) - ) - return .forwardResponseHead(head, pauseRequestBodyStream: true) - } else { - self.state = .running( - .streaming( - expectedBodyLength: expectedBodyLength, - sentBodyBytes: sentBodyBytes, - producer: .producing - ), - .receivingBody(head, .init()) - ) - return .forwardResponseHead(head, pauseRequestBodyStream: false) - } - - case .running(.endSent, .waitingForHead): - self.state = .running(.endSent, .receivingBody(head, .init())) - return .forwardResponseHead(head, pauseRequestBodyStream: false) - - case .running(_, .receivingBody), .running(_, .endReceived), .finished: - preconditionFailure( - "How can we successfully finish the request, before having received a head. Invalid state: \(self.state)" - ) - case .failed: - return .wait - - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - mutating func receivedHTTPResponseBodyPart(_ body: ByteBuffer) -> Action { - switch self.state { - case .initialized, .waitForChannelToBecomeWritable: - preconditionFailure( - "How can we receive a response head before completely sending a request head ourselves. Invalid state: \(self.state)" - ) - - case .running(_, .waitingForHead): - preconditionFailure( - "How can we receive a response body, if we haven't received a head. Invalid state: \(self.state)" - ) - - case .running(let requestState, .receivingBody(let head, var responseStreamState)): - return self.avoidingStateMachineCoW { state -> Action in - responseStreamState.receivedBodyPart(body) - state = .running(requestState, .receivingBody(head, responseStreamState)) - return .wait - } - - case .running(_, .endReceived), .finished: - preconditionFailure( - "How can we successfully finish the request, before having received a head. Invalid state: \(self.state)" - ) - - case .failed: - return .wait - - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - private mutating func receivedHTTPResponseEnd() -> Action { - switch self.state { - case .initialized, .waitForChannelToBecomeWritable: - preconditionFailure( - "How can we receive a response end before completely sending a request head ourselves. Invalid state: \(self.state)" - ) - - case .running(_, .waitingForHead): - preconditionFailure( - "How can we receive a response end, if we haven't a received a head. Invalid state: \(self.state)" - ) - - case .running( - .streaming(let expectedBodyLength, let sentBodyBytes, let producerState), - .receivingBody(let head, var responseStreamState) - ) - where head.status.code < 300: - - return self.avoidingStateMachineCoW { state -> Action in - let (remainingBuffer, connectionAction) = responseStreamState.end() - switch connectionAction { - case .none: - state = .running( - .streaming( - expectedBodyLength: expectedBodyLength, - sentBodyBytes: sentBodyBytes, - producer: producerState - ), - .endReceived - ) - return .forwardResponseBodyParts(remainingBuffer) - case .close: - // If we receive a `.close` as a connectionAction from the responseStreamState - // this means, that the response end was signaled by a connection close. Since - // the request is still uploading, we will not be able to finish the upload. For - // this reason we can fail the request here. - state = .failed(HTTPClientError.remoteConnectionClosed) - return .failRequest(HTTPClientError.remoteConnectionClosed, .close(nil)) - } - } - - case .running(.streaming(_, _, let producerState), .receivingBody(let head, var responseStreamState)): - assert(head.status.code >= 300) - assert( - producerState == .paused, - "Expected to have paused the request body stream, when the head was received. Invalid state: \(self.state)" - ) - - return self.avoidingStateMachineCoW { state -> Action in - // We can ignore the connectionAction from the responseStreamState, since the - // connection should be closed anyway. - let (remainingBuffer, _) = responseStreamState.end() - state = .finished - return .succeedRequest(.close, remainingBuffer) - } - - case .running(.endSent, .receivingBody(_, var responseStreamState)): - return self.avoidingStateMachineCoW { state -> Action in - let (remainingBuffer, action) = responseStreamState.end() - state = .finished - switch action { - case .none: - return .succeedRequest(.none, remainingBuffer) - case .close: - return .succeedRequest(.close, remainingBuffer) - } - } - - case .running(_, .endReceived), .finished: - preconditionFailure( - "How can we receive a response end, if another one was already received. Invalid state: \(self.state)" - ) - - case .failed: - return .wait - - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - mutating func demandMoreResponseBodyParts() -> Action { - switch self.state { - case .initialized, - .running(_, .waitingForHead), - .waitForChannelToBecomeWritable: - preconditionFailure( - "The response is expected to only ask for more data after the response head was forwarded \(self.state)" - ) - - case .running(let requestState, .receivingBody(let head, var responseStreamState)): - return self.avoidingStateMachineCoW { state -> Action in - let action = responseStreamState.demandMoreResponseBodyParts() - state = .running(requestState, .receivingBody(head, responseStreamState)) - return action.toRequestAction() - } - - case .running(_, .endReceived), - .finished, - .failed: - return .wait - - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - mutating func idleReadTimeoutTriggered() -> Action { - switch self.state { - case .initialized, - .waitForChannelToBecomeWritable, - .running(.streaming, _): - preconditionFailure( - "We only schedule idle read timeouts after we have sent the complete request. Invalid state: \(self.state)" - ) - - case .running(.endSent, .waitingForHead), .running(.endSent, .receivingBody): - let error = HTTPClientError.readTimeout - self.state = .failed(error) - return .failRequest(error, .close(nil)) - - case .running(.endSent, .endReceived): - preconditionFailure("Invalid state. This state should be: .finished") - - case .finished, .failed: - return .wait - - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - mutating func idleWriteTimeoutTriggered() -> Action { - switch self.state { - case .initialized, - .waitForChannelToBecomeWritable: - preconditionFailure( - "We only schedule idle write timeouts while the request is being sent. Invalid state: \(self.state)" - ) - - case .running(.streaming, _): - let error = HTTPClientError.writeTimeout - self.state = .failed(error) - return .failRequest(error, .close(nil)) - - case .running(.endSent, _): - preconditionFailure("Invalid state. This state should be: .finished") - - case .finished, .failed: - return .wait - - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - private mutating func startSendingRequest(head: HTTPRequestHead, metadata: RequestFramingMetadata) -> Action { - let length = metadata.body.expectedLength - if length == 0 { - // no body - self.state = .running(.endSent, .waitingForHead) - return .sendRequestHead(head, sendEnd: true) - } else { - self.state = .running( - .streaming(expectedBodyLength: length, sentBodyBytes: 0, producer: .paused), - .waitingForHead - ) - return .sendRequestHead(head, sendEnd: false) - } - } - - mutating func headSent() -> Action { - switch self.state { - case .initialized, .waitForChannelToBecomeWritable, .finished: - preconditionFailure("Not a valid transition after `.sendingHeader`: \(self.state)") - - case .running(.streaming(let expectedBodyLength, let sentBodyBytes, producer: .paused), let responseState): - let startProducing = self.isChannelWritable && expectedBodyLength != sentBodyBytes - self.state = .running( - .streaming( - expectedBodyLength: expectedBodyLength, - sentBodyBytes: sentBodyBytes, - producer: startProducing ? .producing : .paused - ), - responseState - ) - return .notifyRequestHeadSendSuccessfully( - resumeRequestBodyStream: startProducing, - startIdleTimer: false - ) - case .running(.endSent, _): - return .notifyRequestHeadSendSuccessfully(resumeRequestBodyStream: false, startIdleTimer: true) - case .running(.streaming(_, _, producer: .producing), _): - preconditionFailure( - "request body producing can not start before we have successfully send the header \(self.state)" - ) - case .failed: - return .wait - - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } -} - -extension RequestFramingMetadata.Body { - var expectedLength: Int64? { - switch self { - case .fixedSize(let length): return length - case .stream: return nil - } - } -} - -extension HTTPRequestStateMachine { - /// So, uh...this function needs some explaining. - /// - /// While the state machine logic above is great, there is a downside to having all of the state machine data in - /// associated data on enumerations: any modification of that data will trigger copy on write for heap-allocated - /// data. That means that for _every operation on the state machine_ we will CoW our underlying state, which is - /// not good. - /// - /// The way we can avoid this is by using this helper function. It will temporarily set state to a value with no - /// associated data, before attempting the body of the function. It will also verify that the state machine never - /// remains in this bad state. - /// - /// A key note here is that all callers must ensure that they return to a good state before they exit. - /// - /// Sadly, because it's generic and has a closure, we need to force it to be inlined at all call sites, which is - /// not ideal. - @inline(__always) - private mutating func avoidingStateMachineCoW(_ body: (inout State) -> ReturnType) -> ReturnType { - self.state = .modifying - defer { - assert(!self.isModifying) - } - - return body(&self.state) - } - - private var isModifying: Bool { - if case .modifying = self.state { - return true - } else { - return false - } - } -} - -extension HTTPRequestStateMachine.ResponseStreamState.Action { - func toRequestAction() -> HTTPRequestStateMachine.Action { - switch self { - case .read: - return .read - case .wait: - return .wait - } - } -} - -extension HTTPRequestStateMachine: CustomStringConvertible { - var description: String { - switch self.state { - case .initialized: - return "HTTPRequestStateMachine(.initialized, isWritable: \(self.isChannelWritable))" - case .waitForChannelToBecomeWritable: - return "HTTPRequestStateMachine(.waitForChannelToBecomeWritable, isWritable: \(self.isChannelWritable))" - case .running(let requestState, let responseState): - return - "HTTPRequestStateMachine(.running(request: \(requestState), response: \(responseState)), isWritable: \(self.isChannelWritable))" - case .finished: - return "HTTPRequestStateMachine(.finished, isWritable: \(self.isChannelWritable))" - case .failed(let error): - return "HTTPRequestStateMachine(.failed(\(error)), isWritable: \(self.isChannelWritable))" - case .modifying: - return "HTTPRequestStateMachine(.modifying, isWritable: \(self.isChannelWritable))" - } - } -} - -extension HTTPRequestStateMachine.RequestState: CustomStringConvertible { - var description: String { - switch self { - case .streaming(expectedBodyLength: let expected, let sent, let producer): - return ".streaming(sent: \(expected != nil ? String(expected!) : "-"), sent: \(sent), producer: \(producer)" - case .endSent: - return ".endSent" - } - } -} - -extension HTTPRequestStateMachine.ResponseState: CustomStringConvertible { - var description: String { - switch self { - case .waitingForHead: - return ".waitingForHead" - case .receivingBody(let head, let streamState): - return ".receivingBody(\(head), streamState: \(streamState))" - case .endReceived: - return ".endReceived" - } - } -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift b/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift deleted file mode 100644 index 58ba694a7..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/RequestBodyLength.swift +++ /dev/null @@ -1,24 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore - -/// - Note: use `HTTPClientRequest.Body.Length` if you want to expose `RequestBodyLength` publicly -@usableFromInline -internal enum RequestBodyLength: Hashable, Sendable { - /// size of the request body is not known before starting the request - case unknown - /// size of the request body is fixed and exactly `count` bytes - case known(_ count: Int64) -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/RequestFramingMetadata.swift b/Sources/AsyncHTTPClient/ConnectionPool/RequestFramingMetadata.swift deleted file mode 100644 index 033060a99..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/RequestFramingMetadata.swift +++ /dev/null @@ -1,23 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -struct RequestFramingMetadata: Hashable { - enum Body: Hashable { - case stream - case fixedSize(Int64) - } - - var connectionClose: Bool - var body: Body -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/RequestOptions.swift b/Sources/AsyncHTTPClient/ConnectionPool/RequestOptions.swift deleted file mode 100644 index 903f962e5..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/RequestOptions.swift +++ /dev/null @@ -1,44 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore - -struct RequestOptions { - /// The maximal `TimeAmount` that is allowed to pass between `channelRead`s from the Channel. - var idleReadTimeout: TimeAmount? - /// The maximal `TimeAmount` that is allowed to pass between `write`s into the Channel. - var idleWriteTimeout: TimeAmount? - /// DNS overrides. - var dnsOverride: [String: String] - - init( - idleReadTimeout: TimeAmount?, - idleWriteTimeout: TimeAmount?, - dnsOverride: [String: String] - ) { - self.idleReadTimeout = idleReadTimeout - self.idleWriteTimeout = idleWriteTimeout - self.dnsOverride = dnsOverride - } -} - -extension RequestOptions { - static func fromClientConfiguration(_ configuration: HTTPClient.Configuration) -> Self { - RequestOptions( - idleReadTimeout: configuration.timeout.read, - idleWriteTimeout: configuration.timeout.write, - dnsOverride: configuration.dnsOverride - ) - } -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+Backoff.swift b/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+Backoff.swift deleted file mode 100644 index 71d8f15f1..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+Backoff.swift +++ /dev/null @@ -1,68 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore - -#if canImport(Darwin) -import func Darwin.pow -#elseif canImport(Musl) -import func Musl.pow -#elseif canImport(Android) -import func Android.pow -#else -import func Glibc.pow -#endif - -extension HTTPConnectionPool { - /// Calculates the delay for the next connection attempt after the given number of failed `attempts`. - /// - /// Our backoff formula is: 100ms * 1.25^(attempts - 1) that is capped of at 1 minute. - /// This means for: - /// - 1 failed attempt : 100ms - /// - 5 failed attempts: ~300ms - /// - 10 failed attempts: ~930ms - /// - 15 failed attempts: ~2.84s - /// - 20 failed attempts: ~8.67s - /// - 25 failed attempts: ~26s - /// - 29 failed attempts: ~60s (max out) - /// - /// - Parameter attempts: number of failed attempts in a row - /// - Returns: time to wait until trying to establishing a new connection - static func calculateBackoff(failedAttempt attempts: Int) -> TimeAmount { - // Our backoff formula is: 100ms * 1.25^(attempts - 1) that is capped of at 1minute - // This means for: - // - 1 failed attempt : 100ms - // - 5 failed attempts: ~300ms - // - 10 failed attempts: ~930ms - // - 15 failed attempts: ~2.84s - // - 20 failed attempts: ~8.67s - // - 25 failed attempts: ~26s - // - 29 failed attempts: ~60s (max out) - - let start = Double(TimeAmount.milliseconds(100).nanoseconds) - let backoffNanosecondsDouble = start * pow(1.25, Double(attempts - 1)) - - // Cap to 60s _before_ we convert to Int64, to avoid trapping in the Int64 initializer. - let backoffNanoseconds = Int64(min(backoffNanosecondsDouble, Double(TimeAmount.seconds(60).nanoseconds))) - - let backoff = TimeAmount.nanoseconds(backoffNanoseconds) - - // Calculate a 3% jitter range - let jitterRange = (backoff.nanoseconds / 100) * 3 - // Pick a random element from the range +/- jitter range. - let jitter: TimeAmount = .nanoseconds((-jitterRange...jitterRange).randomElement()!) - let jitteredBackoff = backoff + jitter - return jitteredBackoff - } -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP1Connections.swift b/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP1Connections.swift deleted file mode 100644 index 15138a141..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP1Connections.swift +++ /dev/null @@ -1,841 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore - -extension HTTPConnectionPool { - /// Represents the state of a single HTTP/1.1 connection - private struct HTTP1ConnectionState { - enum State { - /// the connection is creating a connection. Valid transitions are to: .backingOff, .idle, and .closed - case starting(maximumUses: Int?) - /// the connection is waiting to retry the establishing a connection. Valid transitions are to: .closed. - /// This means, the connection can be removed from the connections without cancelling external - /// state. The connection state can then be replaced by a new one. - case backingOff - /// the connection is idle for a new request. Valid transitions to: .leased and .closed - case idle(Connection, since: NIODeadline, remainingUses: Int?) - /// the connection is leased and running for a request. Valid transitions to: .idle and .closed - case leased(Connection, remainingUses: Int?) - /// the connection is closed. final state. - case closed - } - - private var state: State - let connectionID: Connection.ID - let eventLoop: EventLoop - - init(connectionID: Connection.ID, eventLoop: EventLoop, maximumUses: Int?) { - self.connectionID = connectionID - self.eventLoop = eventLoop - self.state = .starting(maximumUses: maximumUses) - } - - var isConnecting: Bool { - switch self.state { - case .starting: - return true - case .backingOff, .closed, .idle, .leased: - return false - } - } - - var isBackingOff: Bool { - switch self.state { - case .backingOff: - return true - case .starting, .closed, .idle, .leased: - return false - } - } - - var isIdle: Bool { - switch self.state { - case .idle: - return true - case .backingOff, .starting, .leased, .closed: - return false - } - } - - var idleAndNoRemainingUses: Bool { - switch self.state { - case .idle(_, since: _, let remainingUses): - if let remainingUses = remainingUses { - return remainingUses <= 0 - } else { - return false - } - case .backingOff, .starting, .leased, .closed: - return false - } - } - - var canOrWillBeAbleToExecuteRequests: Bool { - switch self.state { - case .leased, .backingOff, .idle, .starting: - return true - case .closed: - return false - } - } - - var isLeased: Bool { - switch self.state { - case .leased: - return true - case .backingOff, .starting, .idle, .closed: - return false - } - } - - var idleSince: NIODeadline? { - switch self.state { - case .idle(_, since: let idleSince, _): - return idleSince - case .backingOff, .starting, .leased, .closed: - return nil - } - } - - var isClosed: Bool { - switch self.state { - case .closed: - return true - case .idle, .starting, .leased, .backingOff: - return false - } - } - - mutating func connected(_ connection: Connection) { - switch self.state { - case .starting(maximumUses: let maxUses): - self.state = .idle(connection, since: .now(), remainingUses: maxUses) - case .backingOff, .idle, .leased, .closed: - preconditionFailure("Invalid state: \(self.state)") - } - } - - /// The connection failed to start - mutating func failedToConnect() { - switch self.state { - case .starting: - self.state = .backingOff - case .backingOff, .idle, .leased, .closed: - preconditionFailure("Invalid state: \(self.state)") - } - } - - mutating func lease() -> Connection { - switch self.state { - case .idle(let connection, since: _, let remainingUses): - self.state = .leased(connection, remainingUses: remainingUses.map { $0 - 1 }) - return connection - case .backingOff, .starting, .leased, .closed: - preconditionFailure("Invalid state: \(self.state)") - } - } - - mutating func release() { - switch self.state { - case .leased(let connection, let remainingUses): - self.state = .idle(connection, since: .now(), remainingUses: remainingUses) - case .backingOff, .starting, .idle, .closed: - preconditionFailure("Invalid state: \(self.state)") - } - } - - mutating func close() -> Connection { - switch self.state { - case .idle(let connection, since: _, remainingUses: _): - self.state = .closed - return connection - case .backingOff, .starting, .leased, .closed: - preconditionFailure("Invalid state: \(self.state)") - } - } - - mutating func fail() { - switch self.state { - case .starting, .backingOff, .idle, .leased: - self.state = .closed - case .closed: - preconditionFailure("Invalid state: \(self.state)") - } - } - - enum CleanupAction { - case removeConnection - case keepConnection - } - - /// Cleanup the current connection for shutdown. - /// - /// This method is called, when the connections shall shutdown. Depending on the state - /// the connection is in, it adds itself to one of the arrays that are used to signal shutdown - /// intent to the underlying connections. Connections that are backing off can be easily - /// dropped (since, we only need to cancel the backoff timer), connections that are leased - /// need to be cancelled (notifying the `ChannelHandler` that we want to cancel the - /// running request), connections that are idle can be closed right away. Sadly we can't - /// cancel connection starts right now. For this reason we need to wait for them to succeed - /// or fail until we finalize the shutdown. - /// - /// - Parameter context: A cleanup context to add the connection to based on its state. - /// - Returns: A cleanup action indicating if the connection can be removed from the - /// connection list. - func cleanup(_ context: inout CleanupContext) -> CleanupAction { - switch self.state { - case .backingOff: - context.connectBackoff.append(self.connectionID) - return .removeConnection - case .starting: - return .keepConnection - case .idle(let connection, since: _, remainingUses: _): - context.close.append(connection) - return .removeConnection - case .leased(let connection, remainingUses: _): - context.cancel.append(connection) - return .keepConnection - case .closed: - preconditionFailure( - "Unexpected state: Did not expect to have connections with this state in the state machine: \(self.state)" - ) - } - } - - enum MigrateAction { - case removeConnection - case keepConnection - } - - func migrateToHTTP2(_ context: inout HTTP1Connections.HTTP1ToHTTP2MigrationContext) -> MigrateAction { - switch self.state { - case .starting: - context.starting.append((self.connectionID, self.eventLoop)) - return .removeConnection - case .backingOff: - context.backingOff.append((self.connectionID, self.eventLoop)) - return .removeConnection - case .idle(let connection, since: _, remainingUses: _): - // Idle connections can be removed right away - context.close.append(connection) - return .removeConnection - case .leased: - return .keepConnection - case .closed: - preconditionFailure( - "Unexpected state: Did not expect to have connections with this state in the state machine: \(self.state)" - ) - } - } - } - - /// A structure to hold the currently active HTTP/1.1 connections. - /// - /// The general purpose connection pool (pool for requests that don't have special `EventLoop` - /// requirements) will grow up until `maximumConcurrentConnections`. If requests have - /// special `EventLoop` requirements overflow connections might be opened. - /// - /// All connections live in the same `connections` array. In the front are the general purpose - /// connections. In the back (starting with the `overflowIndex`) are the connections for - /// requests with special needs. - struct HTTP1Connections { - /// The maximum number of connections in the general purpose pool. - private let maximumConcurrentConnections: Int - /// A connectionID generator. - private let generator: Connection.ID.Generator - /// The connections states - private var connections: [HTTP1ConnectionState] - /// The index after which you will find the connections for requests with `EventLoop` - /// requirements in `connections`. - private var overflowIndex: Array.Index - /// The number of times each connection can be used before it is closed and replaced. - private let maximumConnectionUses: Int? - - init(maximumConcurrentConnections: Int, generator: Connection.ID.Generator, maximumConnectionUses: Int?) { - self.connections = [] - self.connections.reserveCapacity(min(maximumConcurrentConnections, 1024)) - self.overflowIndex = self.connections.endIndex - self.maximumConcurrentConnections = maximumConcurrentConnections - self.generator = generator - self.maximumConnectionUses = maximumConnectionUses - } - - var stats: Stats { - var stats = Stats() - // all additions here can be unchecked, since we will have at max self.connections.count - // which itself is an Int. For this reason we will never overflow. - for connectionState in self.connections { - if connectionState.isConnecting { - stats.connecting &+= 1 - } else if connectionState.isBackingOff { - stats.backingOff &+= 1 - } else if connectionState.isLeased { - stats.leased &+= 1 - } else if connectionState.isIdle { - stats.idle &+= 1 - } - } - return stats - } - - var isEmpty: Bool { - self.connections.isEmpty - } - - var canGrow: Bool { - self.overflowIndex < self.maximumConcurrentConnections - } - - var startingGeneralPurposeConnections: Int { - var connecting = 0 - for connectionState in self.connections[0.. Int { - self.connections[self.overflowIndex.. Connection.ID { - precondition(self.canGrow) - let connection = HTTP1ConnectionState( - connectionID: self.generator.next(), - eventLoop: eventLoop, - maximumUses: self.maximumConnectionUses - ) - self.connections.insert(connection, at: self.overflowIndex) - self.overflowIndex = self.connections.index(after: self.overflowIndex) - return connection.connectionID - } - - mutating func createNewOverflowConnection(on eventLoop: EventLoop) -> Connection.ID { - let connection = HTTP1ConnectionState( - connectionID: self.generator.next(), - eventLoop: eventLoop, - maximumUses: self.maximumConnectionUses - ) - self.connections.append(connection) - return connection.connectionID - } - - /// A new HTTP/1.1 connection was established. - /// - /// This will put the connection into the idle state. - /// - /// - Parameter connection: The new established connection. - /// - Returns: An index and an IdleConnectionContext to determine the next action for the now idle connection. - /// Call ``leaseConnection(at:)`` or ``closeConnection(at:)`` with the supplied index after - /// this. - mutating func newHTTP1ConnectionEstablished(_ connection: Connection) -> (Int, IdleConnectionContext) { - guard let index = self.connections.firstIndex(where: { $0.connectionID == connection.id }) else { - preconditionFailure("There is a new connection that we didn't request!") - } - precondition( - connection.eventLoop === self.connections[index].eventLoop, - "Expected the new connection to be on EL" - ) - self.connections[index].connected(connection) - let context = self.generateIdleConnectionContextForConnection(at: index) - return (index, context) - } - - /// Move the HTTP1ConnectionState to backingOff. - /// - /// - Parameter connectionID: The connectionID of the failed connection attempt - /// - Returns: The eventLoop on which to schedule the backoff timer - mutating func backoffNextConnectionAttempt(_ connectionID: Connection.ID) -> EventLoop { - guard let index = self.connections.firstIndex(where: { $0.connectionID == connectionID }) else { - preconditionFailure("We tried to create a new connection that we know nothing about?") - } - - self.connections[index].failedToConnect() - return self.connections[index].eventLoop - } - - // MARK: Leasing and releasing - - /// Lease a connection on the preferred EventLoop - /// - /// If no connection is available on the preferred EventLoop, a connection on - /// another eventLoop might be returned, if one is available. - /// - /// - Parameter eventLoop: The preferred EventLoop for the request - /// - Returns: A connection to execute a request on. - mutating func leaseConnection(onPreferred eventLoop: EventLoop) -> Connection? { - guard let index = self.findIdleConnection(onPreferred: eventLoop) else { - return nil - } - - return self.connections[index].lease() - } - - /// Lease a connection on the required EventLoop - /// - /// If no connection is available on the required EventLoop nil is returned. - /// - /// - Parameter eventLoop: The required EventLoop for the request - /// - Returns: A connection to execute a request on. - mutating func leaseConnection(onRequired eventLoop: EventLoop) -> Connection? { - guard let index = self.findIdleConnection(onRequired: eventLoop) else { - return nil - } - - return self.connections[index].lease() - } - - mutating func leaseConnection(at index: Int) -> Connection { - self.connections[index].lease() - } - - func parkConnection(at index: Int) -> (Connection.ID, EventLoop) { - precondition(self.connections[index].isIdle) - return (self.connections[index].connectionID, self.connections[index].eventLoop) - } - - /// A new HTTP/1.1 connection was released. - /// - /// This will put the position into the idle state. - /// - /// - Parameter connectionID: The released connection's id. - /// - Returns: An index and an IdleConnectionContext to determine the next action for the now idle connection. - /// Call ``leaseConnection(at:)`` or ``closeConnection(at:)`` with the supplied index after - /// this. If you want to park the connection no further call is required. - mutating func releaseConnection(_ connectionID: Connection.ID) -> (Int, IdleConnectionContext) { - guard let index = self.connections.firstIndex(where: { $0.connectionID == connectionID }) else { - preconditionFailure("A connection that we don't know was released? Something is very wrong...") - } - - self.connections[index].release() - let context = self.generateIdleConnectionContextForConnection(at: index) - return (index, context) - } - - // MARK: Connection close/removal - - /// Closes the connection at the given index. This will also remove the connection right away. - mutating func closeConnection(at index: Int) -> Connection { - if index < self.overflowIndex { - self.overflowIndex = self.connections.index(before: self.overflowIndex) - } - var connectionState = self.connections.remove(at: index) - return connectionState.close() - } - - mutating func removeConnection(at index: Int) { - precondition(self.connections[index].isClosed) - if index < self.overflowIndex { - self.overflowIndex = self.connections.index(before: self.overflowIndex) - } - self.connections.remove(at: index) - } - - mutating func closeConnectionIfIdle(_ connectionID: Connection.ID) -> Connection? { - guard let index = self.connections.firstIndex(where: { $0.connectionID == connectionID }) else { - // because of a race this connection (connection close runs against trigger of timeout) - // was already removed from the state machine. - return nil - } - - guard self.connections[index].isIdle else { - // connection is not idle anymore, we may have just leased it for a request - return nil - } - - return self.closeConnection(at: index) - } - - mutating func replaceConnection(at index: Int) -> (Connection.ID, EventLoop) { - precondition(self.connections[index].isClosed) - let newConnection = HTTP1ConnectionState( - connectionID: self.generator.next(), - eventLoop: self.connections[index].eventLoop, - maximumUses: self.maximumConnectionUses - ) - - self.connections[index] = newConnection - return (newConnection.connectionID, newConnection.eventLoop) - } - - // MARK: Connection failure - - /// Fail a connection. Call this method, if a connection suddenly closed, did not startup correctly, - /// or the backoff time is done. - /// - /// This will put the position into the closed state. - /// - /// - Parameter connectionID: The failed connection's id. - /// - Returns: An optional index and an IdleConnectionContext to determine the next action for the closed connection. - /// You must call ``removeConnection(at:)`` or ``replaceConnection(at:)`` with the - /// supplied index after this. If nil is returned the connection was closed by the state machine and was - /// therefore already removed. - mutating func failConnection(_ connectionID: Connection.ID) -> (Int, FailedConnectionContext)? { - guard let index = self.connections.firstIndex(where: { $0.connectionID == connectionID }) else { - return nil - } - - let use: ConnectionUse - self.connections[index].fail() - let eventLoop = self.connections[index].eventLoop - let starting: Int - if index < self.overflowIndex { - use = .generalPurpose - starting = self.startingGeneralPurposeConnections - } else { - use = .eventLoop(eventLoop) - starting = self.startingEventLoopConnections(on: eventLoop) - } - - let context = FailedConnectionContext( - eventLoop: eventLoop, - use: use, - connectionsStartingForUseCase: starting - ) - return (index, context) - } - - // MARK: Migration - - mutating func migrateToHTTP2() -> HTTP1ToHTTP2MigrationContext { - var migrationContext = HTTP1ToHTTP2MigrationContext() - let initialOverflowIndex = self.overflowIndex - - self.connections = self.connections.enumerated().compactMap { index, connectionState in - switch connectionState.migrateToHTTP2(&migrationContext) { - case .removeConnection: - // If the connection has an index smaller than the previous overflow index, - // we deal with a general purpose connection. - // For this reason we need to decrement the overflow index. - if index < initialOverflowIndex { - self.overflowIndex = self.connections.index(before: self.overflowIndex) - } - return nil - - case .keepConnection: - return connectionState - } - } - return migrationContext - } - - /// We only handle starting and backing off connection here. - /// All already running connections must be handled by the enclosing state machine. - /// - Parameters: - /// - starting: starting HTTP connections from previous state machine - /// - backingOff: backing off HTTP connections from previous state machine - mutating func migrateFromHTTP2( - starting: [(Connection.ID, EventLoop)], - backingOff: [(Connection.ID, EventLoop)] - ) { - for (connectionID, eventLoop) in starting { - let newConnection = HTTP1ConnectionState( - connectionID: connectionID, - eventLoop: eventLoop, - maximumUses: self.maximumConnectionUses - ) - - self.connections.insert(newConnection, at: self.overflowIndex) - /// If we can grow, we mark the connection as a general purpose connection. - /// Otherwise, it will be an overflow connection which is only used once for requests with a required event loop - if self.canGrow { - self.overflowIndex = self.connections.index(after: self.overflowIndex) - } - } - - for (connectionID, eventLoop) in backingOff { - var backingOffConnection = HTTP1ConnectionState( - connectionID: connectionID, - eventLoop: eventLoop, - maximumUses: self.maximumConnectionUses - ) - // TODO: Maybe we want to add a static init for backing off connections to HTTP1ConnectionState - backingOffConnection.failedToConnect() - - self.connections.insert(backingOffConnection, at: self.overflowIndex) - /// If we can grow, we mark the connection as a general purpose connection. - /// Otherwise, it will be an overflow connection which is only used once for requests with a required event loop - if self.canGrow { - self.overflowIndex = self.connections.index(after: self.overflowIndex) - } - } - } - - /// We will create new connections for each `requiredEventLoopOfPendingRequests` - /// In addition, we also create more general purpose connections if we do not have enough to execute - /// all requests on the given `preferredEventLoopsOfPendingGeneralPurposeRequests` - /// until we reach `maximumConcurrentConnections` - /// - Parameters: - /// - requiredEventLoopsForPendingRequests: - /// event loops for which we have requests with a required event loop. - /// Duplicates are not allowed. - /// - generalPurposeRequestCountPerPreferredEventLoop: - /// request count with no required event loop, - /// grouped by preferred event loop and ordered descending by number of requests - /// - Returns: new connections that must be created - mutating func createConnectionsAfterMigrationIfNeeded( - requiredEventLoopOfPendingRequests: [(EventLoop, Int)], - generalPurposeRequestCountGroupedByPreferredEventLoop: [(EventLoop, Int)] - ) -> [(Connection.ID, EventLoop)] { - // create new connections for requests with a required event loop - - // we may already start connections for those requests and do not want to start too many - let startingRequiredEventLoopConnectionCount = Dictionary( - self.connections[self.overflowIndex.. [(Connection.ID, EventLoop)] in - // We need a connection for each queued request with a required event loop. - // Therefore, we look how many request we have queued for a given `eventLoop` and - // how many connections we are already starting on the given `eventLoop`. - // If we have not enough, we will create additional connections to have at least - // on connection per request. - let connectionsToStart = - requestCount - startingRequiredEventLoopConnectionCount[eventLoop.id, default: 0] - return stride(from: 0, to: connectionsToStart, by: 1).lazy.map { _ in - (self.createNewOverflowConnection(on: eventLoop), eventLoop) - } - } - - // create new connections for requests without a required event loop - - // TODO: improve algorithm to create connections uniformly across all preferred event loops - // while paying attention to the number of queued request per event loop - // Currently we start by creating new connections on the event loop with the most queued - // requests. If we have created enough connections to cover all requests for the first - // event loop we will continue with the event loop with the second most queued requests - // and so on and so forth. The `generalPurposeRequestCountGroupedByPreferredEventLoop` - // array is already ordered so we can just iterate over it without sorting by request count. - let newGeneralPurposeConnections: [(Connection.ID, EventLoop)] = - generalPurposeRequestCountGroupedByPreferredEventLoop - // we do not want to allocated intermediate arrays. - .lazy - // we flatten the grouped list of event loops by lazily repeating the event loop - // for each request. - // As a result we get one event loop per request (`[EventLoop]`). - .flatMap { eventLoop, requestCount in - repeatElement(eventLoop, count: requestCount) - } - // we may already start connections and do not want to start too many - .dropLast(self.startingGeneralPurposeConnections) - // we need to respect the used defined `maximumConcurrentConnections` - .prefix(self.maximumAdditionalGeneralPurposeConnections) - // we now create a connection for each remaining event loop - .map { eventLoop in - (self.createNewConnection(on: eventLoop), eventLoop) - } - - connectionToCreate.append(contentsOf: newGeneralPurposeConnections) - - return connectionToCreate - } - - // MARK: Shutdown - - mutating func shutdown() -> CleanupContext { - var cleanupContext = CleanupContext() - let initialOverflowIndex = self.overflowIndex - - self.connections = self.connections.enumerated().compactMap { index, connectionState in - switch connectionState.cleanup(&cleanupContext) { - case .removeConnection: - // If the connection has an index smaller than the previous overflow index, - // we deal with a general purpose connection. - // For this reason we need to decrement the overflow index. - if index < initialOverflowIndex { - self.overflowIndex = self.connections.index(before: self.overflowIndex) - } - return nil - - case .keepConnection: - return connectionState - } - } - - return cleanupContext - } - - // MARK: - Private functions - - - private func generateIdleConnectionContextForConnection(at index: Int) -> IdleConnectionContext { - precondition(self.connections[index].isIdle) - let eventLoop = self.connections[index].eventLoop - let use: ConnectionUse - if index < self.overflowIndex { - use = .generalPurpose - } else { - use = .eventLoop(eventLoop) - } - let hasNoRemainingUses = self.connections[index].idleAndNoRemainingUses - return IdleConnectionContext(eventLoop: eventLoop, use: use, shouldBeClosed: hasNoRemainingUses) - } - - private func findIdleConnection(onPreferred preferredEL: EventLoop) -> Int? { - var eventLoopMatch: (Int, NIODeadline)? - var goodMatch: (Int, NIODeadline)? - - // To find an appropriate connection we iterate all existing connections. - // While we do this we try to find the best fitting connection for our request. - // - // A perfect match, runs on the same eventLoop and has been idle the shortest amount - // of time (returned the most recently). - // - // An okay match is not on the same eventLoop, and has been idle for the shortest - // time. - for (index, conn) in self.connections.enumerated() { - guard let connReturn = conn.idleSince else { - continue - } - - if conn.eventLoop === preferredEL { - switch eventLoopMatch { - case .none: - eventLoopMatch = (index, connReturn) - case .some((_, let existingMatchReturn)) where connReturn > existingMatchReturn: - eventLoopMatch = (index, connReturn) - default: - break - } - } else { - switch goodMatch { - case .none: - goodMatch = (index, connReturn) - case .some((_, let existingMatchReturn)): - // We don't require a specific eventLoop. For this reason we want to pick a - // matching eventLoop that has been idle the shortest. - if connReturn > existingMatchReturn { - goodMatch = (index, connReturn) - } - } - } - } - - if let (index, _) = eventLoopMatch { - return index - } - - if let (index, _) = goodMatch { - return index - } - - return nil - } - - func findIdleConnection(onRequired requiredEL: EventLoop) -> Int? { - var match: (Int, NIODeadline)? - - // To find an appropriate connection we iterate all existing connections. - // While we do this we try to find the best fitting connection for our request. - // - // A match, runs on the same eventLoop and has been idle the shortest amount of time. - for (index, conn) in self.connections.enumerated() { - // 1. Ensure we are on the correct EL. - guard conn.eventLoop === requiredEL else { - continue - } - - // 2. Ensure the connection is idle - guard let connReturn = conn.idleSince else { - continue - } - - switch match { - case .none: - match = (index, connReturn) - case .some((_, let existingMatchReturn)) where connReturn > existingMatchReturn: - // the currently iterated eventLoop has been idle for a shorter amount than - // the current best match. - match = (index, connReturn) - default: - // the currently iterated eventLoop has been idle for a longer amount than - // the current best match. We continue the iteration. - continue - } - } - - if let (index, _) = match { - return index - } - - return nil - } - - struct Stats { - var idle: Int = 0 - var leased: Int = 0 - var connecting: Int = 0 - var backingOff: Int = 0 - } - } -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP1StateMachine.swift b/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP1StateMachine.swift deleted file mode 100644 index 09b1dc85e..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP1StateMachine.swift +++ /dev/null @@ -1,679 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore - -extension HTTPConnectionPool { - struct HTTP1StateMachine { - typealias Action = HTTPConnectionPool.StateMachine.Action - typealias RequestAction = HTTPConnectionPool.StateMachine.RequestAction - typealias ConnectionMigrationAction = HTTPConnectionPool.StateMachine.ConnectionMigrationAction - typealias EstablishedAction = HTTPConnectionPool.StateMachine.EstablishedAction - typealias EstablishedConnectionAction = HTTPConnectionPool.StateMachine.EstablishedConnectionAction - - private(set) var connections: HTTP1Connections - private(set) var http2Connections: HTTP2Connections? - private var failedConsecutiveConnectionAttempts: Int = 0 - /// the error from the last connection creation - private var lastConnectFailure: Error? - - private(set) var requests: RequestQueue - private(set) var lifecycleState: StateMachine.LifecycleState - /// The property was introduced to fail fast during testing. - /// Otherwise this should always be true and not turned off. - private let retryConnectionEstablishment: Bool - - init( - idGenerator: Connection.ID.Generator, - maximumConcurrentConnections: Int, - retryConnectionEstablishment: Bool, - maximumConnectionUses: Int?, - lifecycleState: StateMachine.LifecycleState - ) { - self.connections = HTTP1Connections( - maximumConcurrentConnections: maximumConcurrentConnections, - generator: idGenerator, - maximumConnectionUses: maximumConnectionUses - ) - self.retryConnectionEstablishment = retryConnectionEstablishment - - self.requests = RequestQueue() - self.lifecycleState = lifecycleState - } - - mutating func migrateFromHTTP2( - http1Connections: HTTP1Connections? = nil, - http2Connections: HTTP2Connections, - requests: RequestQueue, - newHTTP1Connection: Connection - ) -> Action { - let migrationAction = self.migrateConnectionsAndRequestsFromHTTP2( - http1Connections: http1Connections, - http2Connections: http2Connections, - requests: requests - ) - - let newConnectionAction = self._newHTTP1ConnectionEstablished( - newHTTP1Connection - ) - - return .init( - request: newConnectionAction.request, - connection: .combined(migrationAction, newConnectionAction.connection) - ) - } - - private mutating func migrateConnectionsAndRequestsFromHTTP2( - http1Connections: HTTP1Connections?, - http2Connections: HTTP2Connections, - requests: RequestQueue - ) -> ConnectionMigrationAction { - precondition(self.connections.isEmpty, "expected an empty state machine but connections are not empty") - precondition( - self.http2Connections == nil, - "expected an empty state machine but http2Connections are not nil" - ) - precondition(self.requests.isEmpty, "expected an empty state machine but requests are not empty") - - self.requests = requests - - // we may have remaining open http1 connections from a pervious migration to http2 - if let http1Connections = http1Connections { - self.connections = http1Connections - } - - var http2Connections = http2Connections - let migration = http2Connections.migrateToHTTP1() - - self.connections.migrateFromHTTP2( - starting: migration.starting, - backingOff: migration.backingOff - ) - - let createConnections = self.connections.createConnectionsAfterMigrationIfNeeded( - requiredEventLoopOfPendingRequests: requests.requestCountGroupedByRequiredEventLoop(), - generalPurposeRequestCountGroupedByPreferredEventLoop: - requests.generalPurposeRequestCountGroupedByPreferredEventLoop() - ) - - if !http2Connections.isEmpty { - self.http2Connections = http2Connections - } - - // TODO: Potentially cancel unneeded bootstraps (Needs cancellable ClientBootstrap) - - return .init( - closeConnections: migration.close, - createConnections: createConnections - ) - } - - // MARK: - Events - - - mutating func executeRequest(_ request: Request) -> Action { - switch self.lifecycleState { - case .running: - if let eventLoop = request.requiredEventLoop { - return self.executeRequestOnRequiredEventLoop(request, eventLoop: eventLoop) - } else { - return self.executeRequestOnPreferredEventLoop(request, eventLoop: request.preferredEventLoop) - } - case .shuttingDown, .shutDown: - // it is fairly unlikely that this condition is met, since the ConnectionPoolManager - // also fails new requests immediately, if it is shutting down. However there might - // be race conditions in which a request passes through a running connection pool - // manager, but hits a connection pool that is already shutting down. - // - // (Order in one lock does not guarantee order in the next lock!) - return .init( - request: .failRequest(request, HTTPClientError.alreadyShutdown, cancelTimeout: false), - connection: .none - ) - } - } - - private mutating func executeRequestOnPreferredEventLoop(_ request: Request, eventLoop: EventLoop) -> Action { - if let connection = self.connections.leaseConnection(onPreferred: eventLoop) { - return .init( - request: .executeRequest(request, connection, cancelTimeout: false), - connection: .cancelTimeoutTimer(connection.id) - ) - } - - // No matter what we do now, the request will need to wait! - self.requests.push(request) - let requestAction: StateMachine.RequestAction = .scheduleRequestTimeout( - for: request, - on: eventLoop - ) - - if !self.connections.canGrow { - // all connections are busy and there is no room for more connections, we need to wait! - return .init(request: requestAction, connection: .none) - } - - // if we are not at max connections, we may want to create a new connection - if self.connections.startingGeneralPurposeConnections >= self.requests.generalPurposeCount { - // If there are at least as many connections starting as we have request queued, we - // don't need to create a new connection. we just need to wait. - return .init(request: requestAction, connection: .none) - } - - // There are not enough connections starting for the current waiting request count. We - // should create a new one. - let newConnectionID = self.connections.createNewConnection(on: eventLoop) - - return .init( - request: requestAction, - connection: .createConnection(newConnectionID, on: eventLoop) - ) - } - - private mutating func executeRequestOnRequiredEventLoop(_ request: Request, eventLoop: EventLoop) -> Action { - if let connection = self.connections.leaseConnection(onRequired: eventLoop) { - return .init( - request: .executeRequest(request, connection, cancelTimeout: false), - connection: .cancelTimeoutTimer(connection.id) - ) - } - - // No matter what we do now, the request will need to wait! - self.requests.push(request) - let requestAction: StateMachine.RequestAction = .scheduleRequestTimeout( - for: request, - on: eventLoop - ) - - let starting = self.connections.startingEventLoopConnections(on: eventLoop) - let waiting = self.requests.count(for: eventLoop) - - if starting >= waiting { - // There are already as many connections starting as we need for the waiting - // requests. A new connection doesn't need to be created. - return .init(request: requestAction, connection: .none) - } - - // There are not enough connections starting for the number of requests in the queue. - // We should create a new connection. - let newConnectionID = self.connections.createNewOverflowConnection(on: eventLoop) - - return .init( - request: requestAction, - connection: .createConnection(newConnectionID, on: eventLoop) - ) - } - - mutating func newHTTP1ConnectionEstablished(_ connection: Connection) -> Action { - .init(self._newHTTP1ConnectionEstablished(connection)) - } - - private mutating func _newHTTP1ConnectionEstablished(_ connection: Connection) -> EstablishedAction { - self.failedConsecutiveConnectionAttempts = 0 - self.lastConnectFailure = nil - let (index, context) = self.connections.newHTTP1ConnectionEstablished(connection) - return self.nextActionForIdleConnection(at: index, context: context) - } - - mutating func failedToCreateNewConnection(_ error: Error, connectionID: Connection.ID) -> Action { - self.failedConsecutiveConnectionAttempts += 1 - self.lastConnectFailure = error - - switch self.lifecycleState { - case .running: - guard self.retryConnectionEstablishment else { - guard let (index, _) = self.connections.failConnection(connectionID) else { - preconditionFailure( - "A connection attempt failed, that the state machine knows nothing about. Somewhere state was lost." - ) - } - self.connections.removeConnection(at: index) - - return .init( - request: self.failAllRequests(reason: error), - connection: .none - ) - } - // We don't care how many waiting requests we have at this point, we will schedule a - // retry. More tasks, may appear until the backoff has completed. The final - // decision about the retry will be made in `connectionCreationBackoffDone(_:)` - let eventLoop = self.connections.backoffNextConnectionAttempt(connectionID) - - let backoff = calculateBackoff(failedAttempt: self.failedConsecutiveConnectionAttempts) - return .init( - request: .none, - connection: .scheduleBackoffTimer(connectionID, backoff: backoff, on: eventLoop) - ) - - case .shuttingDown: - guard let (index, context) = self.connections.failConnection(connectionID) else { - preconditionFailure("Failed to create a connection that is unknown to us?") - } - return self.nextActionForFailedConnection(at: index, context: context) - - case .shutDown: - preconditionFailure("The pool is already shutdown all connections must already been torn down") - } - } - - mutating func waitingForConnectivity(_ error: Error, connectionID: Connection.ID) -> Action { - self.lastConnectFailure = error - - return .init(request: .none, connection: .none) - } - - mutating func connectionCreationBackoffDone(_ connectionID: Connection.ID) -> Action { - switch self.lifecycleState { - case .running: - // The naming of `failConnection` is a little confusing here. All it does is moving the - // connection state from `.backingOff` to `.closed` here. It also returns the - // connection's index. - guard let (index, context) = self.connections.failConnection(connectionID) else { - preconditionFailure("Backing off a connection that is unknown to us?") - } - // In `nextActionForFailedConnection` a decision will be made whether the failed - // connection should be replaced or removed. - return self.nextActionForFailedConnection(at: index, context: context) - - case .shuttingDown, .shutDown: - // There might be a race between shutdown and a backoff timer firing. On thread A - // we might call shutdown which removes the backoff timer. On thread B the backoff - // timer might fire at the same time and be blocked by the state lock. In this case - // we would look for the backoff timer that was removed just before by the shutdown. - return .none - } - } - - mutating func connectionIdleTimeout(_ connectionID: Connection.ID) -> Action { - guard let connection = self.connections.closeConnectionIfIdle(connectionID) else { - // because of a race this connection (connection close runs against trigger of timeout) - // was already removed from the state machine. - return .none - } - - precondition( - self.lifecycleState == .running, - "If we are shutting down, we must not have any idle connections" - ) - - return .init( - request: .none, - connection: .closeConnection(connection, isShutdown: .no) - ) - } - - mutating func http1ConnectionReleased(_ connectionID: Connection.ID) -> Action { - let (index, context) = self.connections.releaseConnection(connectionID) - return .init(self.nextActionForIdleConnection(at: index, context: context)) - } - - /// A connection has been unexpectedly closed - mutating func http1ConnectionClosed(_ connectionID: Connection.ID) -> Action { - guard let (index, context) = self.connections.failConnection(connectionID) else { - // When a connection close is initiated by the connection pool, the connection will - // still report its close to the state machine. In those cases we must ignore the - // event. - return .none - } - return self.nextActionForFailedConnection(at: index, context: context) - } - - mutating func timeoutRequest(_ requestID: Request.ID) -> Action { - // 1. check requests in queue - if let request = self.requests.remove(requestID) { - var error: Error = HTTPClientError.getConnectionFromPoolTimeout - if let lastError = self.lastConnectFailure { - error = lastError - } else if !self.connections.hasActiveConnections { - error = HTTPClientError.connectTimeout - } - return .init( - request: .failRequest(request, error, cancelTimeout: false), - connection: .none - ) - } - - // 2. This point is reached, because the request may have already been scheduled. A - // connection might have become available shortly before the request timeout timer - // fired. - return .none - } - - mutating func cancelRequest(_ requestID: Request.ID) -> Action { - // 1. check requests in queue - if let request = self.requests.remove(requestID) { - // Use the last connection error to let the user know why the request was never scheduled - let error = self.lastConnectFailure ?? HTTPClientError.cancelled - return .init( - request: .failRequest(request, error, cancelTimeout: true), - connection: .none - ) - } - - // 2. This is point is reached, because the request may already have been forwarded to - // an idle connection. In this case the connection will need to handle the - // cancellation. - return .none - } - - mutating func shutdown() -> Action { - precondition(self.lifecycleState == .running, "Shutdown must only be called once") - - // If we have remaining request queued, we should fail all of them with a cancelled - // error. - let waitingRequests = self.requests.removeAll() - - var requestAction: StateMachine.RequestAction = .none - if !waitingRequests.isEmpty { - requestAction = .failRequestsAndCancelTimeouts(waitingRequests, HTTPClientError.cancelled) - } - - // clean up the connections, we can cleanup now! - let cleanupContext = self.connections.shutdown() - - // If there aren't any more connections, everything is shutdown - let isShutdown: StateMachine.ConnectionAction.IsShutdown - let unclean = !(cleanupContext.cancel.isEmpty && waitingRequests.isEmpty) - if self.connections.isEmpty && self.http2Connections == nil { - self.lifecycleState = .shutDown - isShutdown = .yes(unclean: unclean) - } else { - self.lifecycleState = .shuttingDown(unclean: unclean) - isShutdown = .no - } - - return .init( - request: requestAction, - connection: .cleanupConnections(cleanupContext, isShutdown: isShutdown) - ) - } - - // MARK: - Private Methods - - - // MARK: Idle connection management - - private mutating func nextActionForIdleConnection( - at index: Int, - context: HTTP1Connections.IdleConnectionContext - ) -> EstablishedAction { - switch self.lifecycleState { - case .running: - // Close the connection if it's expired. - if context.shouldBeClosed { - let connection = self.connections.closeConnection(at: index) - return .init( - request: .none, - connection: .closeConnection(connection, isShutdown: .no) - ) - } else { - switch context.use { - case .generalPurpose: - return self.nextActionForIdleGeneralPurposeConnection(at: index, context: context) - case .eventLoop: - return self.nextActionForIdleEventLoopConnection(at: index, context: context) - } - } - case .shuttingDown(let unclean): - assert(self.requests.isEmpty) - let connection = self.connections.closeConnection(at: index) - if self.connections.isEmpty && self.http2Connections == nil { - return .init( - request: .none, - connection: .closeConnection(connection, isShutdown: .yes(unclean: unclean)) - ) - } - return .init( - request: .none, - connection: .closeConnection(connection, isShutdown: .no) - ) - - case .shutDown: - preconditionFailure("It the pool is already shutdown, all connections must have been torn down.") - } - } - - private mutating func nextActionForIdleGeneralPurposeConnection( - at index: Int, - context: HTTP1Connections.IdleConnectionContext - ) -> EstablishedAction { - // 1. Check if there are waiting requests in the general purpose queue - if let request = self.requests.popFirst(for: nil) { - return .init( - request: .executeRequest(request, self.connections.leaseConnection(at: index), cancelTimeout: true), - connection: .none - ) - } - - // 2. Check if there are waiting requests in the matching eventLoop queue - if let request = self.requests.popFirst(for: context.eventLoop) { - return .init( - request: .executeRequest(request, self.connections.leaseConnection(at: index), cancelTimeout: true), - connection: .none - ) - } - - // 3. Create a timeout timer to ensure the connection is closed if it is idle for too - // long. - let (connectionID, eventLoop) = self.connections.parkConnection(at: index) - return .init( - request: .none, - connection: .scheduleTimeoutTimer(connectionID, on: eventLoop) - ) - } - - private mutating func nextActionForIdleEventLoopConnection( - at index: Int, - context: HTTP1Connections.IdleConnectionContext - ) -> EstablishedAction { - // Check if there are waiting requests in the matching eventLoop queue - if let request = self.requests.popFirst(for: context.eventLoop) { - return .init( - request: .executeRequest(request, self.connections.leaseConnection(at: index), cancelTimeout: true), - connection: .none - ) - } - - // TBD: What do we want to do, if there are more requests in the general purpose queue? - // For now, we don't care. The general purpose connections will pick those up - // eventually. - // - // If there is no more eventLoop bound work, we close the eventLoop bound connections. - // We don't park them. - return .init( - request: .none, - connection: .closeConnection(self.connections.closeConnection(at: index), isShutdown: .no) - ) - } - - // MARK: Failed/Closed connection management - - private mutating func nextActionForFailedConnection( - at index: Int, - context: HTTP1Connections.FailedConnectionContext - ) -> Action { - switch self.lifecycleState { - case .running: - switch context.use { - case .generalPurpose: - return self.nextActionForFailedGeneralPurposeConnection(at: index, context: context) - case .eventLoop: - return self.nextActionForFailedEventLoopConnection(at: index, context: context) - } - - case .shuttingDown(let unclean): - assert(self.requests.isEmpty) - self.connections.removeConnection(at: index) - if self.connections.isEmpty && self.http2Connections == nil { - return .init( - request: .none, - connection: .cleanupConnections(.init(), isShutdown: .yes(unclean: unclean)) - ) - } - return .none - - case .shutDown: - preconditionFailure("If the pool is already shutdown, all connections must have been torn down.") - } - } - - private mutating func nextActionForFailedGeneralPurposeConnection( - at index: Int, - context: HTTP1Connections.FailedConnectionContext - ) -> Action { - if context.connectionsStartingForUseCase < self.requests.generalPurposeCount { - // if we have more requests queued up, than we have starting connections, we should - // create a new connection - let (newConnectionID, newEventLoop) = self.connections.replaceConnection(at: index) - return .init( - request: .none, - connection: .createConnection(newConnectionID, on: newEventLoop) - ) - } - self.connections.removeConnection(at: index) - return .none - } - - private mutating func nextActionForFailedEventLoopConnection( - at index: Int, - context: HTTP1Connections.FailedConnectionContext - ) -> Action { - if context.connectionsStartingForUseCase < self.requests.count(for: context.eventLoop) { - // if we have more requests queued up, than we have starting connections, we should - // create a new connection - let (newConnectionID, newEventLoop) = self.connections.replaceConnection(at: index) - return .init( - request: .none, - connection: .createConnection(newConnectionID, on: newEventLoop) - ) - } - self.connections.removeConnection(at: index) - return .none - } - - private mutating func failAllRequests(reason error: Error) -> RequestAction { - let allRequests = self.requests.removeAll() - guard !allRequests.isEmpty else { - return .none - } - return .failRequestsAndCancelTimeouts(allRequests, error) - } - - // MARK: HTTP2 - - mutating func newHTTP2MaxConcurrentStreamsReceived(_ connectionID: Connection.ID, newMaxStreams: Int) -> Action - { - // The `http2Connections` are optional here: - // Connections report events back to us, if they are in a shutdown that was - // initiated by the state machine. For this reason this callback might be invoked - // even though all references to HTTP2Connections have already been cleared. - _ = self.http2Connections?.newHTTP2MaxConcurrentStreamsReceived(connectionID, newMaxStreams: newMaxStreams) - return .none - } - - mutating func http2ConnectionGoAwayReceived(_ connectionID: Connection.ID) -> Action { - // The `http2Connections` are optional here: - // Connections report events back to us, if they are in a shutdown that was - // initiated by the state machine. For this reason this callback might be invoked - // even though all references to HTTP2Connections have already been cleared. - _ = self.http2Connections?.goAwayReceived(connectionID) - return .none - } - - mutating func http2ConnectionClosed(_ connectionID: Connection.ID) -> Action { - switch self.lifecycleState { - case .running: - _ = self.http2Connections?.failConnection(connectionID) - if self.http2Connections?.isEmpty == true { - self.http2Connections = nil - } - return .none - - case .shuttingDown(let unclean): - assert(self.requests.isEmpty) - _ = self.http2Connections?.failConnection(connectionID) - if self.http2Connections?.isEmpty == true { - self.http2Connections = nil - } - if self.connections.isEmpty && self.http2Connections == nil { - return .init( - request: .none, - connection: .cleanupConnections(.init(), isShutdown: .yes(unclean: unclean)) - ) - } - return .init( - request: .none, - connection: .none - ) - - case .shutDown: - preconditionFailure("It the pool is already shutdown, all connections must have been torn down.") - } - } - - mutating func http2ConnectionStreamClosed(_ connectionID: Connection.ID) -> Action { - // It is save to bang the http2Connections here. If we get this callback but we don't have - // http2 connections something has gone terribly wrong. - switch self.lifecycleState { - case .running: - let (index, context) = self.http2Connections!.releaseStream(connectionID) - guard context.isIdle else { - return .none - } - - let connection = self.http2Connections!.closeConnection(at: index) - if self.http2Connections!.isEmpty { - self.http2Connections = nil - } - return .init( - request: .none, - connection: .closeConnection(connection, isShutdown: .no) - ) - - case .shuttingDown(let unclean): - assert(self.requests.isEmpty) - let (index, context) = self.http2Connections!.releaseStream(connectionID) - guard context.isIdle else { - return .none - } - - let connection = self.http2Connections!.closeConnection(at: index) - if self.http2Connections!.isEmpty { - self.http2Connections = nil - } - if self.connections.isEmpty && self.http2Connections == nil { - return .init( - request: .none, - connection: .closeConnection(connection, isShutdown: .yes(unclean: unclean)) - ) - } - return .init( - request: .none, - connection: .closeConnection(connection, isShutdown: .no) - ) - - case .shutDown: - preconditionFailure("It the pool is already shutdown, all connections must have been torn down.") - } - } - } -} - -extension HTTPConnectionPool.HTTP1StateMachine: CustomStringConvertible { - var description: String { - let stats = self.connections.stats - let queued = self.requests.count - - return - "connections: [connecting: \(stats.connecting) | backoff: \(stats.backingOff) | leased: \(stats.leased) | idle: \(stats.idle)], queued: \(queued)" - } -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP2Connections.swift b/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP2Connections.swift deleted file mode 100644 index dbb6b2d30..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP2Connections.swift +++ /dev/null @@ -1,819 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore - -extension HTTPConnectionPool { - private struct HTTP2ConnectionState { - private enum State { - /// the pool is establishing a connection. Valid transitions are to: .backingOff, .active and .closed - case starting(maximumUses: Int?) - /// the connection is waiting to retry to establish a connection. Valid transitions are to .closed. - /// From .closed a new connection state must be created for a retry. - case backingOff - /// the connection is active and is able to run requests. Valid transitions are to: .draining and .closed - case active(Connection, maxStreams: Int, usedStreams: Int, lastIdle: NIODeadline, remainingUses: Int?) - /// the connection is active and is running requests. No new requests must be scheduled. - /// Valid transitions to: .draining and .closed - case draining(Connection, maxStreams: Int, usedStreams: Int) - /// the connection is closed - case closed - } - - var isStartingOrBackingOff: Bool { - switch self.state { - case .starting, .backingOff: - return true - case .active, .draining, .closed: - return false - } - } - - var canOrWillBeAbleToExecuteRequests: Bool { - switch self.state { - case .starting, .backingOff, .active: - return true - case .draining, .closed: - return false - } - } - - var isStartingOrActive: Bool { - switch self.state { - case .starting, .active: - return true - case .draining, .backingOff, .closed: - return false - } - } - - /// A connection is established and can potentially execute requests if not all streams are leased - var isActive: Bool { - switch self.state { - case .active: - return true - case .starting, .backingOff, .draining, .closed: - return false - } - } - - /// A request can be scheduled on the connection - var isAvailable: Bool { - switch self.state { - case .active(_, let maxStreams, let usedStreams, _, let remainingUses): - if let remainingUses = remainingUses { - return usedStreams < maxStreams && remainingUses > 0 - } else { - return usedStreams < maxStreams - } - case .starting, .backingOff, .draining, .closed: - return false - } - } - - /// The connection is active, but there are no running requests on the connection. - /// Every idle connection is available, but not every available connection is idle. - var isIdle: Bool { - switch self.state { - case .active(_, _, let usedStreams, _, _): - return usedStreams == 0 - case .starting, .backingOff, .draining, .closed: - return false - } - } - - var isClosed: Bool { - switch self.state { - case .starting, .backingOff, .draining, .active: - return false - case .closed: - return true - } - } - - private var state: State - let eventLoop: EventLoop - let connectionID: Connection.ID - - /// should be called after the connection was successfully established - /// - Parameters: - /// - conn: HTTP2 connection - /// - maxStreams: max streams settings from the server - /// - Returns: number of available streams which can be leased - mutating func connected(_ conn: Connection, maxStreams: Int) -> Int { - switch self.state { - case .active, .draining, .backingOff, .closed: - preconditionFailure("Invalid state: \(self.state)") - - case .starting(let maxUses): - self.state = .active( - conn, - maxStreams: maxStreams, - usedStreams: 0, - lastIdle: .now(), - remainingUses: maxUses - ) - if let maxUses = maxUses { - return min(maxStreams, maxUses) - } else { - return maxStreams - } - } - } - - /// should be called after receiving new http2 settings from the server - /// - Parameters: - /// - maxStreams: max streams settings from the server - /// - Returns: number of available streams which can be leased - mutating func newMaxConcurrentStreams(_ maxStreams: Int) -> Int { - switch self.state { - case .starting, .backingOff, .closed: - preconditionFailure("Invalid state for updating max concurrent streams: \(self.state)") - - case .active(let conn, _, let usedStreams, let lastIdle, let remainingUses): - self.state = .active( - conn, - maxStreams: maxStreams, - usedStreams: usedStreams, - lastIdle: lastIdle, - remainingUses: remainingUses - ) - let availableStreams = max(maxStreams - usedStreams, 0) - if let remainingUses = remainingUses { - return min(remainingUses, availableStreams) - } else { - return availableStreams - } - - case .draining(let conn, _, let usedStreams): - self.state = .draining(conn, maxStreams: maxStreams, usedStreams: usedStreams) - return 0 - } - } - - mutating func goAwayReceived() -> EventLoop { - switch self.state { - case .starting, .backingOff, .closed: - preconditionFailure("Invalid state for draining a connection: \(self.state)") - - case .active(let conn, let maxStreams, let usedStreams, _, _): - self.state = .draining(conn, maxStreams: maxStreams, usedStreams: usedStreams) - return conn.eventLoop - - case .draining(let conn, _, _): - // we could potentially receive another go away while we drain all active streams and we just ignore it - return conn.eventLoop - } - } - - /// The connection failed to start - mutating func failedToConnect() { - switch self.state { - case .starting: - self.state = .backingOff - case .backingOff, .active, .draining, .closed: - preconditionFailure("Invalid state: \(self.state)") - } - } - - mutating func fail() { - switch self.state { - case .starting, .active, .backingOff, .draining: - self.state = .closed - case .closed: - preconditionFailure("Invalid state: \(self.state)") - } - } - - mutating func lease(_ count: Int) -> Connection { - switch self.state { - case .starting, .backingOff, .draining, .closed: - preconditionFailure("Invalid state for leasing a stream: \(self.state)") - - case .active(let conn, let maxStreams, var usedStreams, let lastIdle, let remainingUses): - usedStreams += count - precondition(usedStreams <= maxStreams, "tried to lease a connection which is not available") - precondition( - remainingUses.map { $0 >= count } ?? true, - "tried to lease streams from a connection which does not have enough remaining streams" - ) - self.state = .active( - conn, - maxStreams: maxStreams, - usedStreams: usedStreams, - lastIdle: lastIdle, - remainingUses: remainingUses.map { $0 - count } - ) - return conn - } - } - - /// should be called after a request has finished and the stream can be used again for a new request - /// - Returns: number of available streams which can be leased - mutating func release() -> Int { - switch self.state { - case .starting, .backingOff, .closed: - preconditionFailure("Invalid state: \(self.state)") - - case .active(let conn, let maxStreams, var usedStreams, var lastIdle, let remainingUses): - precondition(usedStreams > 0, "we cannot release more streams than we have leased") - usedStreams &-= 1 - if usedStreams == 0 { - lastIdle = .now() - } - - self.state = .active( - conn, - maxStreams: maxStreams, - usedStreams: usedStreams, - lastIdle: lastIdle, - remainingUses: remainingUses - ) - let availableStreams = max(maxStreams &- usedStreams, 0) - if let remainingUses = remainingUses { - return min(availableStreams, remainingUses) - } else { - return availableStreams - } - - case .draining(let conn, let maxStreams, var usedStreams): - precondition(usedStreams > 0, "we cannot release more streams than we have leased") - usedStreams &-= 1 - self.state = .draining(conn, maxStreams: maxStreams, usedStreams: usedStreams) - return 0 - } - } - - mutating func close() -> Connection { - switch self.state { - case .active(let conn, _, 0, _, _): - self.state = .closed - return conn - - case .starting, .backingOff, .draining, .closed, .active: - preconditionFailure("Invalid state for closing a connection: \(self.state)") - } - } - - enum CleanupAction { - case removeConnection - case keepConnection - } - - /// Cleanup the current connection for shutdown. - /// - /// This method is called, when the connections shall shutdown. Depending on the state - /// the connection is in, it adds itself to one of the arrays that are used to signal shutdown - /// intent to the underlying connections. Connections that are backing off can be easily - /// dropped (since, we only need to cancel the backoff timer), connections that are leased - /// need to be cancelled (notifying the `ChannelHandler` that we want to cancel the - /// running request), connections that are idle can be closed right away. Sadly we can't - /// cancel connection starts right now. For this reason we need to wait for them to succeed - /// or fail until we finalize the shutdown. - /// - /// - Parameter context: A cleanup context to add the connection to based on its state. - /// - Returns: A cleanup action indicating if the connection can be removed from the - /// connection list. - func cleanup(_ context: inout CleanupContext) -> CleanupAction { - switch self.state { - case .starting: - return .keepConnection - - case .backingOff: - context.connectBackoff.append(self.connectionID) - return .removeConnection - - case .active(let connection, _, let usedStreams, _, _): - precondition(usedStreams >= 0) - if usedStreams == 0 { - context.close.append(connection) - return .removeConnection - } else { - context.cancel.append(connection) - return .keepConnection - } - - case .draining(let connection, _, _): - context.cancel.append(connection) - return .keepConnection - - case .closed: - preconditionFailure( - "Unexpected state for cleanup: Did not expect to have closed connections in the state machine." - ) - } - } - - func addStats(into stats: inout HTTP2Connections.Stats) { - switch self.state { - case .starting: - stats.startingConnections &+= 1 - - case .backingOff: - stats.backingOffConnections &+= 1 - - case .active(_, let maxStreams, let usedStreams, _, _): - stats.availableStreams += max(maxStreams - usedStreams, 0) - stats.leasedStreams += usedStreams - stats.availableConnections &+= 1 - precondition(usedStreams >= 0) - if usedStreams == 0 { - stats.idleConnections &+= 1 - } - case .draining(_, _, let usedStreams): - stats.drainingConnections &+= 1 - stats.leasedStreams += usedStreams - precondition(usedStreams >= 0) - case .closed: - break - } - } - - enum MigrateAction { - case removeConnection - case keepConnection - } - - func migrateToHTTP1( - context: inout HTTP2Connections.HTTP2ToHTTP1MigrationContext - ) -> MigrateAction { - switch self.state { - case .starting: - context.starting.append((self.connectionID, self.eventLoop)) - return .removeConnection - - case .active(let connection, _, let usedStreams, _, _): - precondition(usedStreams >= 0) - if usedStreams == 0 { - context.close.append(connection) - return .removeConnection - } else { - return .keepConnection - } - - case .draining: - return .keepConnection - - case .backingOff: - context.backingOff.append((self.connectionID, self.eventLoop)) - return .removeConnection - - case .closed: - preconditionFailure( - "Unexpected state: Did not expect to have connections with this state in the state machine: \(self.state)" - ) - } - } - - init(connectionID: Connection.ID, eventLoop: EventLoop, maximumUses: Int?) { - self.connectionID = connectionID - self.eventLoop = eventLoop - self.state = .starting(maximumUses: maximumUses) - } - } - - struct HTTP2Connections { - /// A connectionID generator. - private let generator: Connection.ID.Generator - /// The connections states - private var connections: [HTTP2ConnectionState] - /// The number of times each connection can be used before it is closed and replaced. - private let maximumConnectionUses: Int? - - var isEmpty: Bool { - self.connections.isEmpty - } - - var stats: Stats { - self.connections.reduce(into: Stats()) { stats, connection in - connection.addStats(into: &stats) - } - } - - init(generator: Connection.ID.Generator, maximumConnectionUses: Int?) { - self.generator = generator - self.connections = [] - self.maximumConnectionUses = maximumConnectionUses - } - - // MARK: Migration - - /// We only handle starting and backing off connection here. - /// All already running connections must be handled by the enclosing state machine. - /// - Parameters: - /// - starting: starting HTTP connections from previous state machine - /// - backingOff: backing off HTTP connections from previous state machine - mutating func migrateFromHTTP1( - starting: [(Connection.ID, EventLoop)], - backingOff: [(Connection.ID, EventLoop)] - ) { - for (connectionID, eventLoop) in starting { - let newConnection = HTTP2ConnectionState( - connectionID: connectionID, - eventLoop: eventLoop, - maximumUses: self.maximumConnectionUses - ) - self.connections.append(newConnection) - } - - for (connectionID, eventLoop) in backingOff { - var backingOffConnection = HTTP2ConnectionState( - connectionID: connectionID, - eventLoop: eventLoop, - maximumUses: self.maximumConnectionUses - ) - // TODO: Maybe we want to add a static init for backing off connections to HTTP2ConnectionState - backingOffConnection.failedToConnect() - self.connections.append(backingOffConnection) - } - } - - /// We will create new connections for `requiredEventLoopsOfPendingRequests` - /// if we do not already have a connection that can or will be able to execute requests on the given event loop. - /// - Parameters: - /// - requiredEventLoopsForPendingRequests: event loops for which we have requests with a required event loop. Duplicates are not allowed. - /// - Returns: new connections that need to be created - mutating func createConnectionsAfterMigrationIfNeeded( - requiredEventLoopsOfPendingRequests: [EventLoop] - ) -> [(Connection.ID, EventLoop)] { - // create new connections for requests with a required event loop - let eventLoopsWithConnectionThatCanOrWillBeAbleToExecuteRequests = Set( - self.connections.lazy - .filter { - $0.canOrWillBeAbleToExecuteRequests - }.map { - $0.eventLoop.id - } - ) - return requiredEventLoopsOfPendingRequests.compactMap { eventLoop -> (Connection.ID, EventLoop)? in - guard !eventLoopsWithConnectionThatCanOrWillBeAbleToExecuteRequests.contains(eventLoop.id) - else { return nil } - let connectionID = self.createNewConnection(on: eventLoop) - return (connectionID, eventLoop) - } - } - - struct HTTP2ToHTTP1MigrationContext { - var backingOff: [(Connection.ID, EventLoop)] = [] - var starting: [(Connection.ID, EventLoop)] = [] - var close: [Connection] = [] - } - - mutating func migrateToHTTP1() -> HTTP2ToHTTP1MigrationContext { - var context = HTTP2ToHTTP1MigrationContext() - self.connections.removeAll { connection in - switch connection.migrateToHTTP1(context: &context) { - case .removeConnection: - return true - case .keepConnection: - return false - } - } - return context - } - - // MARK: Connection creation - - /// true if one ore more connections are active - var hasActiveConnections: Bool { - self.connections.contains { $0.isActive } - } - - /// used in general purpose connection scenarios to check if at least one connection is starting, backing off or active - var hasConnectionThatCanOrWillBeAbleToExecuteRequests: Bool { - self.connections.contains { $0.canOrWillBeAbleToExecuteRequests } - } - - /// used in eventLoop scenarios. does at least one connection exist for this eventLoop, or should we create a new one? - /// - Parameter eventLoop: connection `EventLoop` to search for - /// - Returns: true if at least one connection is starting, backing off or active for the given `eventLoop` - func hasConnectionThatCanOrWillBeAbleToExecuteRequests(for eventLoop: EventLoop) -> Bool { - self.connections.contains { - $0.eventLoop === eventLoop && $0.canOrWillBeAbleToExecuteRequests - } - } - - func hasActiveConnection(for eventLoop: EventLoop) -> Bool { - self.connections.contains { - $0.eventLoop === eventLoop && $0.isActive - } - } - - /// used after backoff is done to determine if we need to create a new connection - /// - Parameters: - /// - eventLoop: connection `EventLoop` to search for - /// - Returns: if we have a starting or active general purpose connection and if we have also one for the given `eventLoop` - func backingOffTimerDone( - for eventLoop: EventLoop - ) -> RetryConnectionCreationContext { - var hasGeneralPurposeConnection: Bool = false - var hasConnectionOnSpecifiedEventLoop: Bool = false - for connection in self.connections { - guard connection.isStartingOrActive else { continue } - hasGeneralPurposeConnection = true - guard connection.eventLoop === eventLoop else { continue } - hasConnectionOnSpecifiedEventLoop = true - break - } - return RetryConnectionCreationContext( - hasGeneralPurposeConnection: hasGeneralPurposeConnection, - hasConnectionOnSpecifiedEventLoop: hasConnectionOnSpecifiedEventLoop - ) - } - - mutating func createNewConnection(on eventLoop: EventLoop) -> Connection.ID { - assert( - !self.hasConnectionThatCanOrWillBeAbleToExecuteRequests(for: eventLoop), - "we should not create more than one connection per event loop" - ) - - let connection = HTTP2ConnectionState( - connectionID: self.generator.next(), - eventLoop: eventLoop, - maximumUses: self.maximumConnectionUses - ) - self.connections.append(connection) - return connection.connectionID - } - - /// A new HTTP/2 connection was established. - /// - /// This will put the connection into the idle state. - /// - /// - Parameter connection: The new established connection. - /// - Returns: An index and an ``EstablishedConnectionContext`` to determine the next action for the now idle connection. - /// Call ``leaseStreams(at:count:)`` or ``closeConnection(at:)`` with the supplied index after - /// this. - mutating func newHTTP2ConnectionEstablished( - _ connection: Connection, - maxConcurrentStreams: Int - ) -> (Int, EstablishedConnectionContext) { - guard let index = self.connections.firstIndex(where: { $0.connectionID == connection.id }) else { - preconditionFailure("There is a new connection that we didn't request!") - } - precondition( - connection.eventLoop === self.connections[index].eventLoop, - "Expected the new connection to be on EL" - ) - let availableStreams = self.connections[index].connected(connection, maxStreams: maxConcurrentStreams) - let context = EstablishedConnectionContext( - availableStreams: availableStreams, - eventLoop: connection.eventLoop, - isIdle: self.connections[index].isIdle, - connectionID: connection.id - ) - return (index, context) - } - - /// Move the connection state to backingOff. - /// - /// - Parameter connectionID: The connectionID of the failed connection attempt - /// - Returns: The eventLoop on which to schedule the backoff timer - /// - Precondition: connection needs to be in the `.starting` state - mutating func backoffNextConnectionAttempt(_ connectionID: Connection.ID) -> EventLoop { - guard let index = self.connections.firstIndex(where: { $0.connectionID == connectionID }) else { - preconditionFailure("We tried to create a new connection that we know nothing about?") - } - - self.connections[index].failedToConnect() - return self.connections[index].eventLoop - } - - // MARK: Connection lifecycle events - - /// Sets the connection with the given `connectionId` to the draining state. - /// - Returns: the `EventLoop` to create a new connection on if applicable - /// - Precondition: connection with given `connectionId` must be either `.active` or already in the `.draining` state - mutating func goAwayReceived(_ connectionID: Connection.ID) -> GoAwayContext? { - guard let index = self.connections.firstIndex(where: { $0.connectionID == connectionID }) else { - // When a connection close is initiated by the connection pool (e.g. because the - // connection was idle for too long), the connection will still report further - // events to the state machine even though we don't care about its state anymore. - // - // This is because the HTTP2Connection has a strong let reference to its delegate. - return nil - } - let eventLoop = self.connections[index].goAwayReceived() - return GoAwayContext(eventLoop: eventLoop) - } - - /// Update the maximum number of concurrent streams for the given connection. - /// - Parameters: - /// - connectionID: The connectionID for which we received new settings - /// - newMaxStreams: new maximum concurrent streams - /// - Returns: index of the connection and new number of available streams in the `EstablishedConnectionContext` - /// - Precondition: Connections must be in the `.active` or `.draining` state. - mutating func newHTTP2MaxConcurrentStreamsReceived( - _ connectionID: Connection.ID, - newMaxStreams: Int - ) -> (Int, EstablishedConnectionContext)? { - guard let index = self.connections.firstIndex(where: { $0.connectionID == connectionID }) else { - // When a connection close is initiated by the connection pool (e.g. because the - // connection was idle for too long), the connection will still report its events to - // the state machine and hence to this `HTTP2Connections` struct. In those cases we - // must ignore the event. - return nil - } - let availableStreams = self.connections[index].newMaxConcurrentStreams(newMaxStreams) - let context = EstablishedConnectionContext( - availableStreams: availableStreams, - eventLoop: self.connections[index].eventLoop, - isIdle: self.connections[index].isIdle, - connectionID: connectionID - ) - return (index, context) - } - - // MARK: Leasing and releasing - - mutating func leaseStream(onPreferred eventLoop: EventLoop) -> (Connection, LeasedStreamContext)? { - guard let index = self.findAvailableConnection(onPreferred: eventLoop) else { return nil } - return self.leaseStreams(at: index, count: 1) - } - - /// tries to find an available connection on the preferred `eventLoop`. If it can't find one with the given `eventLoop`, it returns the first available connection - private func findAvailableConnection(onPreferred eventLoop: EventLoop) -> Int? { - var availableConnectionIndex: Int? - for (offset, connection) in self.connections.enumerated() { - guard connection.isAvailable else { continue } - if connection.eventLoop === eventLoop { - return self.connections.index(self.connections.startIndex, offsetBy: offset) - } else if availableConnectionIndex == nil { - availableConnectionIndex = self.connections.index(self.connections.startIndex, offsetBy: offset) - } - } - return availableConnectionIndex - } - - mutating func leaseStream(onRequired eventLoop: EventLoop) -> (Connection, LeasedStreamContext)? { - guard let index = self.findAvailableConnection(onRequired: eventLoop) else { return nil } - return self.leaseStreams(at: index, count: 1) - } - - /// tries to find an available connection on the required `eventLoop` - private func findAvailableConnection(onRequired eventLoop: EventLoop) -> Int? { - self.connections.firstIndex(where: { $0.eventLoop === eventLoop && $0.isAvailable }) - } - - /// lease `count` streams after connections establishment - /// - Parameters: - /// - index: index of the connection you got by calling `newHTTP2ConnectionEstablished(_:maxConcurrentStreams:)` - /// - count: number of streams you want to lease. You get the current available streams from the `EstablishedConnectionContext` which `newHTTP2ConnectionEstablished(_:maxConcurrentStreams:)` returns - /// - Returns: connection to execute `count` requests on - /// - precondition: `index` needs to be valid. `count` must be greater than or equal to *1* and not exceed the number of available streams. - mutating func leaseStreams(at index: Int, count: Int) -> (Connection, LeasedStreamContext) { - precondition(count >= 1, "stream lease count must be greater than or equal to 1") - let isIdle = self.connections[index].isIdle - let connection = self.connections[index].lease(count) - let context = LeasedStreamContext(wasIdle: isIdle) - return (connection, context) - } - - mutating func releaseStream(_ connectionID: Connection.ID) -> (Int, EstablishedConnectionContext) { - guard let index = self.connections.firstIndex(where: { $0.connectionID == connectionID }) else { - preconditionFailure("We tried to release a connection we do not know anything about") - } - let availableStreams = self.connections[index].release() - let context = EstablishedConnectionContext( - availableStreams: availableStreams, - eventLoop: self.connections[index].eventLoop, - isIdle: self.connections[index].isIdle, - connectionID: connectionID - ) - return (index, context) - } - - // MARK: Connection close/removal - - /// Closes the connection at the given index. This will also remove the connection right away. - /// - Parameter index: index of the connection which we get from `releaseStream(_:)` - /// - Returns: closed and removed connection - mutating func closeConnection(at index: Int) -> Connection { - let connection = self.connections[index].close() - self.removeConnection(at: index) - return connection - } - - /// removes a closed connection. - /// - Parameter index: index of the connection which we get from `failConnection(_:)` - /// - Precondition: connection must be closed - mutating func removeConnection(at index: Int) { - precondition(self.connections[index].isClosed, "We tried to remove a connection which is not closed") - self.connections.remove(at: index) - } - - mutating func closeConnectionIfIdle(_ connectionID: Connection.ID) -> Connection? { - guard let index = self.connections.firstIndex(where: { $0.connectionID == connectionID }) else { - // because of a race this connection (connection close runs against trigger of timeout) - // was already removed from the state machine. - return nil - } - guard self.connections[index].isIdle else { - // connection is not idle anymore, we may have just leased it for a request - return nil - } - return self.closeConnection(at: index) - } - - /// replaces a closed connection by creating a new starting connection. - /// - Parameter index: index of the connection which we got from `failConnection(_:)` - /// - Precondition: connection must be closed - mutating func createNewConnectionByReplacingClosedConnection(at index: Int) -> (Connection.ID, EventLoop) { - precondition(self.connections[index].isClosed) - let newConnection = HTTP2ConnectionState( - connectionID: self.generator.next(), - eventLoop: self.connections[index].eventLoop, - maximumUses: self.maximumConnectionUses - ) - - self.connections[index] = newConnection - return (newConnection.connectionID, newConnection.eventLoop) - } - - mutating func failConnection(_ connectionID: Connection.ID) -> (Int, FailedConnectionContext)? { - guard let index = self.connections.firstIndex(where: { $0.connectionID == connectionID }) else { - // When a connection close is initiated by the connection pool (e.g. because the - // connection was idle for too long), the connection will still report its close to - // the state machine and then to this `HTTP2Connections` struct. In those cases we - // must ignore the event. - return nil - } - self.connections[index].fail() - let eventLoop = self.connections[index].eventLoop - let context = FailedConnectionContext(eventLoop: eventLoop) - return (index, context) - } - - mutating func shutdown() -> CleanupContext { - var cleanupContext = CleanupContext() - self.connections.removeAll(where: { connectionState in - switch connectionState.cleanup(&cleanupContext) { - case .removeConnection: - return true - case .keepConnection: - return false - } - }) - return cleanupContext - } - - // MARK: Result structs - - struct RetryConnectionCreationContext { - /// true if at least one connection is starting or active regardless of the event loop. - let hasGeneralPurposeConnection: Bool - - /// true if at least one connection is starting or active for the given `eventLoop` - let hasConnectionOnSpecifiedEventLoop: Bool - } - - /// Information around a connection which is either in the .active or .draining state. - struct EstablishedConnectionContext { - /// number of streams which can be leased - var availableStreams: Int - /// The eventLoop the connection is running on. - var eventLoop: EventLoop - /// true if no stream is leased - var isIdle: Bool - /// id of the connection - var connectionID: Connection.ID - } - - struct LeasedStreamContext { - /// true if the connection was idle before leasing the stream - var wasIdle: Bool - } - - struct GoAwayContext { - /// The eventLoop the connection is running on. - var eventLoop: EventLoop - } - - /// Information around the failed/closed connection. - struct FailedConnectionContext { - /// The eventLoop the connection ran on. - var eventLoop: EventLoop - } - - struct Stats: Equatable { - var startingConnections: Int = 0 - var backingOffConnections: Int = 0 - var idleConnections: Int = 0 - var availableConnections: Int = 0 - var drainingConnections: Int = 0 - var leasedStreams: Int = 0 - var availableStreams: Int = 0 - } - } -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP2StateMachine.swift b/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP2StateMachine.swift deleted file mode 100644 index 2372cab4b..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP2StateMachine.swift +++ /dev/null @@ -1,627 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOHTTP2 - -extension HTTPConnectionPool { - struct HTTP2StateMachine { - typealias Action = HTTPConnectionPool.StateMachine.Action - typealias RequestAction = HTTPConnectionPool.StateMachine.RequestAction - typealias ConnectionMigrationAction = HTTPConnectionPool.StateMachine.ConnectionMigrationAction - typealias EstablishedAction = HTTPConnectionPool.StateMachine.EstablishedAction - typealias EstablishedConnectionAction = HTTPConnectionPool.StateMachine.EstablishedConnectionAction - - private var lastConnectFailure: Error? - private var failedConsecutiveConnectionAttempts = 0 - - private(set) var connections: HTTP2Connections - private(set) var http1Connections: HTTP1Connections? - - private(set) var requests: RequestQueue - - private let idGenerator: Connection.ID.Generator - - private(set) var lifecycleState: StateMachine.LifecycleState - /// The property was introduced to fail fast during testing. - /// Otherwise this should always be true and not turned off. - private let retryConnectionEstablishment: Bool - - init( - idGenerator: Connection.ID.Generator, - retryConnectionEstablishment: Bool, - lifecycleState: StateMachine.LifecycleState, - maximumConnectionUses: Int? - ) { - self.idGenerator = idGenerator - self.requests = RequestQueue() - - self.connections = HTTP2Connections( - generator: idGenerator, - maximumConnectionUses: maximumConnectionUses - ) - self.lifecycleState = lifecycleState - self.retryConnectionEstablishment = retryConnectionEstablishment - } - - mutating func migrateFromHTTP1( - http1Connections: HTTP1Connections, - http2Connections: HTTP2Connections? = nil, - requests: RequestQueue, - newHTTP2Connection: Connection, - maxConcurrentStreams: Int - ) -> Action { - let migrationAction = self.migrateConnectionsAndRequestsFromHTTP1( - http1Connections: http1Connections, - http2Connections: http2Connections, - requests: requests - ) - - let newConnectionAction = self._newHTTP2ConnectionEstablished( - newHTTP2Connection, - maxConcurrentStreams: maxConcurrentStreams - ) - - return .init( - request: newConnectionAction.request, - connection: .combined(migrationAction, newConnectionAction.connection) - ) - } - - private mutating func migrateConnectionsAndRequestsFromHTTP1( - http1Connections: HTTP1Connections, - http2Connections: HTTP2Connections?, - requests: RequestQueue - ) -> ConnectionMigrationAction { - precondition(self.connections.isEmpty, "expected an empty state machine but connections are not empty") - precondition( - self.http1Connections == nil, - "expected an empty state machine but http1Connections are not nil" - ) - precondition(self.requests.isEmpty, "expected an empty state machine but requests are not empty") - - self.requests = requests - - // we may have remaining open http2 connections from a pervious migration to http1 - if let http2Connections = http2Connections { - self.connections = http2Connections - } - - var http1Connections = http1Connections // make http1Connections mutable - let context = http1Connections.migrateToHTTP2() - self.connections.migrateFromHTTP1( - starting: context.starting, - backingOff: context.backingOff - ) - - let createConnections = self.connections.createConnectionsAfterMigrationIfNeeded( - requiredEventLoopsOfPendingRequests: requests.eventLoopsWithPendingRequests() - ) - - if !http1Connections.isEmpty { - self.http1Connections = http1Connections - } - - // TODO: Potentially cancel unneeded bootstraps (Needs cancellable ClientBootstrap) - return .init( - closeConnections: context.close, - createConnections: createConnections - ) - } - - mutating func executeRequest(_ request: Request) -> Action { - switch self.lifecycleState { - case .running: - if let eventLoop = request.requiredEventLoop { - return self.executeRequest(request, onRequired: eventLoop) - } else { - return self.executeRequest(request, onPreferred: request.preferredEventLoop) - } - case .shutDown, .shuttingDown: - // it is fairly unlikely that this condition is met, since the ConnectionPoolManager - // also fails new requests immediately, if it is shutting down. However there might - // be race conditions in which a request passes through a running connection pool - // manager, but hits a connection pool that is already shutting down. - // - // (Order in one lock does not guarantee order in the next lock!) - return .init( - request: .failRequest(request, HTTPClientError.alreadyShutdown, cancelTimeout: false), - connection: .none - ) - } - } - - private mutating func executeRequest( - _ request: Request, - onRequired eventLoop: EventLoop - ) -> Action { - if let (connection, context) = self.connections.leaseStream(onRequired: eventLoop) { - /// 1. we have a stream available and can execute the request immediately - if context.wasIdle { - return .init( - request: .executeRequest(request, connection, cancelTimeout: false), - connection: .cancelTimeoutTimer(connection.id) - ) - } else { - return .init( - request: .executeRequest(request, connection, cancelTimeout: false), - connection: .none - ) - } - } - /// 2. No available stream so we definitely need to wait until we have one - self.requests.push(request) - - if self.connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests(for: eventLoop) { - /// 3. we already have a connection, we just need to wait until until it becomes available - return .init( - request: .scheduleRequestTimeout(for: request, on: eventLoop), - connection: .none - ) - } else { - /// 4. we do *not* have a connection, need to create a new one and wait until it is connected. - let connectionId = self.connections.createNewConnection(on: eventLoop) - return .init( - request: .scheduleRequestTimeout(for: request, on: eventLoop), - connection: .createConnection(connectionId, on: eventLoop) - ) - } - } - - private mutating func executeRequest( - _ request: Request, - onPreferred eventLoop: EventLoop - ) -> Action { - if let (connection, context) = self.connections.leaseStream(onPreferred: eventLoop) { - /// 1. we have a stream available and can execute the request immediately - if context.wasIdle { - return .init( - request: .executeRequest(request, connection, cancelTimeout: false), - connection: .cancelTimeoutTimer(connection.id) - ) - } else { - return .init( - request: .executeRequest(request, connection, cancelTimeout: false), - connection: .none - ) - } - } - /// 2. No available stream so we definitely need to wait until we have one - self.requests.push(request) - - if self.connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests { - /// 3. we already have a connection, we just need to wait until until it becomes available - return .init( - request: .scheduleRequestTimeout(for: request, on: eventLoop), - connection: .none - ) - } else { - /// 4. we do *not* have a connection, need to create a new one and wait until it is connected. - let connectionId = self.connections.createNewConnection(on: eventLoop) - return .init( - request: .scheduleRequestTimeout(for: request, on: eventLoop), - connection: .createConnection(connectionId, on: eventLoop) - ) - } - } - - mutating func newHTTP2ConnectionEstablished(_ connection: Connection, maxConcurrentStreams: Int) -> Action { - .init(self._newHTTP2ConnectionEstablished(connection, maxConcurrentStreams: maxConcurrentStreams)) - } - - private mutating func _newHTTP2ConnectionEstablished( - _ connection: Connection, - maxConcurrentStreams: Int - ) -> EstablishedAction { - self.failedConsecutiveConnectionAttempts = 0 - self.lastConnectFailure = nil - if self.connections.hasActiveConnection(for: connection.eventLoop) { - guard let (index, _) = self.connections.failConnection(connection.id) else { - preconditionFailure("we have established a new connection that we know nothing about?") - } - self.connections.removeConnection(at: index) - return .init( - request: .none, - connection: .closeConnection(connection, isShutdown: .no) - ) - } else { - let (index, context) = self.connections.newHTTP2ConnectionEstablished( - connection, - maxConcurrentStreams: maxConcurrentStreams - ) - return self.nextActionForAvailableConnection(at: index, context: context) - } - } - - private mutating func nextActionForAvailableConnection( - at index: Int, - context: HTTP2Connections.EstablishedConnectionContext - ) -> EstablishedAction { - switch self.lifecycleState { - case .running: - // We prioritise requests with a required event loop over those without a requirement. - // This can cause starvation for request without a required event loop. - // We should come up with a better algorithm in the future. - - var requestsToExecute = self.requests.popFirst(max: context.availableStreams, for: context.eventLoop) - let remainingAvailableStreams = context.availableStreams - requestsToExecute.count - // use the remaining available streams for requests without a required event loop - requestsToExecute += self.requests.popFirst(max: remainingAvailableStreams, for: nil) - - let requestAction = { () -> HTTPConnectionPool.StateMachine.RequestAction in - if requestsToExecute.isEmpty { - return .none - } else { - // we can only lease streams if the connection has available streams. - // Otherwise we might crash even if we try to lease zero streams, - // because the connection might already be in the draining state. - let (connection, _) = self.connections.leaseStreams(at: index, count: requestsToExecute.count) - return .executeRequestsAndCancelTimeouts(requestsToExecute, connection) - } - }() - - let connectionAction = { () -> EstablishedConnectionAction in - if context.isIdle, requestsToExecute.isEmpty { - return .scheduleTimeoutTimer(context.connectionID, on: context.eventLoop) - } else { - return .none - } - }() - - return .init( - request: requestAction, - connection: connectionAction - ) - case .shuttingDown(let unclean): - guard context.isIdle else { - return .none - } - - let connection = self.connections.closeConnection(at: index) - if self.http1Connections == nil, self.connections.isEmpty { - return .init( - request: .none, - connection: .closeConnection(connection, isShutdown: .yes(unclean: unclean)) - ) - } - return .init( - request: .none, - connection: .closeConnection(connection, isShutdown: .no) - ) - case .shutDown: - preconditionFailure("It the pool is already shutdown, all connections must have been torn down.") - } - } - - mutating func newHTTP2MaxConcurrentStreamsReceived(_ connectionID: Connection.ID, newMaxStreams: Int) -> Action - { - guard - let (index, context) = self.connections.newHTTP2MaxConcurrentStreamsReceived( - connectionID, - newMaxStreams: newMaxStreams - ) - else { - // When a connection close is initiated by the connection pool, the connection will - // still report further events (like newMaxConcurrentStreamsReceived) to the state - // machine. In those cases we must ignore the event. - return .none - } - return .init(self.nextActionForAvailableConnection(at: index, context: context)) - } - - mutating func http2ConnectionGoAwayReceived(_ connectionID: Connection.ID) -> Action { - guard let context = self.connections.goAwayReceived(connectionID) else { - // When a connection close is initiated by the connection pool, the connection will - // still report further events (like GOAWAY received) to the state machine. In those - // cases we must ignore the event. - return .none - } - return self.nextActionForClosingConnection(on: context.eventLoop) - } - - mutating func http2ConnectionClosed(_ connectionID: Connection.ID) -> Action { - guard let (index, context) = self.connections.failConnection(connectionID) else { - // When a connection close is initiated by the connection pool, the connection will - // still report its close to the state machine. In those cases we must ignore the - // event. - return .none - } - return self.nextActionForFailedConnection(at: index, on: context.eventLoop) - } - - private mutating func nextActionForFailedConnection(at index: Int, on eventLoop: EventLoop) -> Action { - switch self.lifecycleState { - case .running: - // we do not know if we have created this connection for a request with a required - // event loop or not. However, we do not need this information and can infer - // if we need to create a new connection because we will only ever create one connection - // per event loop for required event loop requests and only need one connection for - // general purpose requests. - - // precompute if we have starting or active connections to only iterate once over `self.connections` - let context = self.connections.backingOffTimerDone(for: eventLoop) - - // we need to start a new on connection in two cases: - let needGeneralPurposeConnection = - // 1. if we have general purpose requests - !self.requests.isEmpty(for: nil) - // and no connection starting or active - && !context.hasGeneralPurposeConnection - - let needRequiredEventLoopConnection = - // 2. or if we have requests for a required event loop - !self.requests.isEmpty(for: eventLoop) - // and no connection starting or active for the given event loop - && !context.hasConnectionOnSpecifiedEventLoop - - guard needGeneralPurposeConnection || needRequiredEventLoopConnection else { - // otherwise we can remove the connection - self.connections.removeConnection(at: index) - return .none - } - - let (newConnectionID, previousEventLoop) = self.connections - .createNewConnectionByReplacingClosedConnection(at: index) - precondition(previousEventLoop === eventLoop) - - return .init( - request: .none, - connection: .createConnection(newConnectionID, on: eventLoop) - ) - - case .shuttingDown(let unclean): - assert(self.requests.isEmpty) - self.connections.removeConnection(at: index) - if self.connections.isEmpty { - return .init( - request: .none, - connection: .cleanupConnections(.init(), isShutdown: .yes(unclean: unclean)) - ) - } - return .none - - case .shutDown: - preconditionFailure("If the pool is already shutdown, all connections must have been torn down.") - } - } - - private mutating func nextActionForClosingConnection(on eventLoop: EventLoop) -> Action { - switch self.lifecycleState { - case .running: - let hasPendingRequest = !self.requests.isEmpty(for: eventLoop) || !self.requests.isEmpty(for: nil) - guard hasPendingRequest else { - return .none - } - - let newConnectionID = self.connections.createNewConnection(on: eventLoop) - - return .init( - request: .none, - connection: .createConnection(newConnectionID, on: eventLoop) - ) - case .shutDown, .shuttingDown: - return .none - } - } - - mutating func http2ConnectionStreamClosed(_ connectionID: Connection.ID) -> Action { - let (index, context) = self.connections.releaseStream(connectionID) - return .init(self.nextActionForAvailableConnection(at: index, context: context)) - } - - mutating func failedToCreateNewConnection(_ error: Error, connectionID: Connection.ID) -> Action { - self.failedConsecutiveConnectionAttempts += 1 - self.lastConnectFailure = error - - switch self.lifecycleState { - case .running: - guard self.retryConnectionEstablishment else { - guard let (index, _) = self.connections.failConnection(connectionID) else { - preconditionFailure( - "A connection attempt failed, that the state machine knows nothing about. Somewhere state was lost." - ) - } - self.connections.removeConnection(at: index) - - return .init( - request: self.failAllRequests(reason: error), - connection: .none - ) - } - - let eventLoop = self.connections.backoffNextConnectionAttempt(connectionID) - let backoff = calculateBackoff(failedAttempt: self.failedConsecutiveConnectionAttempts) - return .init( - request: .none, - connection: .scheduleBackoffTimer(connectionID, backoff: backoff, on: eventLoop) - ) - case .shuttingDown: - guard let (index, context) = self.connections.failConnection(connectionID) else { - preconditionFailure( - "A connection attempt failed, that the state machine knows nothing about. Somewhere state was lost." - ) - } - return self.nextActionForFailedConnection(at: index, on: context.eventLoop) - case .shutDown: - preconditionFailure("If the pool is already shutdown, all connections must have been torn down.") - } - } - - mutating func waitingForConnectivity(_ error: Error, connectionID: Connection.ID) -> Action { - self.lastConnectFailure = error - - return .init(request: .none, connection: .none) - } - - mutating func connectionCreationBackoffDone(_ connectionID: Connection.ID) -> Action { - // The naming of `failConnection` is a little confusing here. All it does is moving the - // connection state from `.backingOff` to `.closed` here. It also returns the - // connection's index. - guard let (index, context) = self.connections.failConnection(connectionID) else { - preconditionFailure("Backing off a connection that is unknown to us?") - } - return self.nextActionForFailedConnection(at: index, on: context.eventLoop) - } - - private mutating func failAllRequests(reason error: Error) -> RequestAction { - let allRequests = self.requests.removeAll() - guard !allRequests.isEmpty else { - return .none - } - return .failRequestsAndCancelTimeouts(allRequests, error) - } - - mutating func timeoutRequest(_ requestID: Request.ID) -> Action { - // 1. check requests in queue - if let request = self.requests.remove(requestID) { - var error: Error = HTTPClientError.getConnectionFromPoolTimeout - if let lastError = self.lastConnectFailure { - error = lastError - } else if !self.connections.hasActiveConnections { - error = HTTPClientError.connectTimeout - } - return .init( - request: .failRequest(request, error, cancelTimeout: false), - connection: .none - ) - } - - // 2. This point is reached, because the request may have already been scheduled. A - // connection might have become available shortly before the request timeout timer - // fired. - return .none - } - - mutating func cancelRequest(_ requestID: Request.ID) -> Action { - // 1. check requests in queue - if let request = self.requests.remove(requestID) { - // Use the last connection error to let the user know why the request was never scheduled - let error = self.lastConnectFailure ?? HTTPClientError.cancelled - return .init( - request: .failRequest(request, error, cancelTimeout: true), - connection: .none - ) - } - - // 2. This is point is reached, because the request may already have been forwarded to - // an idle connection. In this case the connection will need to handle the - // cancellation. - return .none - } - - mutating func connectionIdleTimeout(_ connectionID: Connection.ID) -> Action { - guard let connection = connections.closeConnectionIfIdle(connectionID) else { - // because of a race this connection (connection close runs against trigger of timeout) - // was already removed from the state machine. - return .none - } - - precondition( - self.lifecycleState == .running, - "If we are shutting down, we must not have any idle connections" - ) - - return .init( - request: .none, - connection: .closeConnection(connection, isShutdown: .no) - ) - } - - mutating func http1ConnectionClosed(_ connectionID: Connection.ID) -> Action { - guard let index = self.http1Connections?.failConnection(connectionID)?.0 else { - return .none - } - self.http1Connections!.removeConnection(at: index) - if self.http1Connections!.isEmpty { - self.http1Connections = nil - } - switch self.lifecycleState { - case .running: - return .none - case .shuttingDown(let unclean): - if self.http1Connections == nil, self.connections.isEmpty { - return .init( - request: .none, - connection: .cleanupConnections(.init(), isShutdown: .yes(unclean: unclean)) - ) - } else { - return .none - } - case .shutDown: - preconditionFailure("If the pool is already shutdown, all connections must have been torn down.") - } - } - - mutating func http1ConnectionReleased(_ connectionID: Connection.ID) -> Action { - // It is save to bang the http1Connections here. If we get this callback but we don't have - // http1 connections something has gone terribly wrong. - let (index, _) = self.http1Connections!.releaseConnection(connectionID) - // Any http1 connection that becomes idle should be closed right away after the transition - // to http2. - let connection = self.http1Connections!.closeConnection(at: index) - guard self.http1Connections!.isEmpty else { - return .init(request: .none, connection: .closeConnection(connection, isShutdown: .no)) - } - // if there are no more http1Connections, we can remove the struct. - self.http1Connections = nil - - // we must also check, if we are shutting down. Was this maybe out last connection? - switch self.lifecycleState { - case .running: - return .init(request: .none, connection: .closeConnection(connection, isShutdown: .no)) - case .shuttingDown(let unclean): - if self.connections.isEmpty { - // if the http2connections are empty as well, there are no more connections. Shutdown completed. - return .init( - request: .none, - connection: .closeConnection(connection, isShutdown: .yes(unclean: unclean)) - ) - } else { - return .init(request: .none, connection: .closeConnection(connection, isShutdown: .no)) - } - case .shutDown: - preconditionFailure("If the pool is already shutdown, all connections must have been torn down.") - } - } - - mutating func shutdown() -> Action { - // If we have remaining request queued, we should fail all of them with a cancelled - // error. - let waitingRequests = self.requests.removeAll() - - var requestAction: StateMachine.RequestAction = .none - if !waitingRequests.isEmpty { - requestAction = .failRequestsAndCancelTimeouts(waitingRequests, HTTPClientError.cancelled) - } - - // clean up the connections, we can cleanup now! - let cleanupContext = self.connections.shutdown() - - // If there aren't any more connections, everything is shutdown - let isShutdown: StateMachine.ConnectionAction.IsShutdown - let unclean = !(cleanupContext.cancel.isEmpty && waitingRequests.isEmpty && self.http1Connections == nil) - if self.connections.isEmpty && self.http1Connections == nil { - isShutdown = .yes(unclean: unclean) - self.lifecycleState = .shutDown - } else { - isShutdown = .no - self.lifecycleState = .shuttingDown(unclean: unclean) - } - return .init( - request: requestAction, - connection: .cleanupConnections(cleanupContext, isShutdown: isShutdown) - ) - } - } -} diff --git a/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+RequestQueue.swift b/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+RequestQueue.swift deleted file mode 100644 index 110533f53..000000000 --- a/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+RequestQueue.swift +++ /dev/null @@ -1,207 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore - -private struct HashableEventLoop: Hashable { - static func == (lhs: HashableEventLoop, rhs: HashableEventLoop) -> Bool { - lhs.eventLoop === rhs.eventLoop - } - - init(_ eventLoop: EventLoop) { - self.eventLoop = eventLoop - } - - let eventLoop: EventLoop - func hash(into hasher: inout Hasher) { - self.eventLoop.id.hash(into: &hasher) - } -} - -extension HTTPConnectionPool { - /// A struct to store all queued requests. - struct RequestQueue { - private var generalPurposeQueue: CircularBuffer - private var eventLoopQueues: [EventLoopID: CircularBuffer] - - init() { - self.generalPurposeQueue = CircularBuffer(initialCapacity: 32) - self.eventLoopQueues = [:] - } - - var count: Int { - self.generalPurposeQueue.count + self.eventLoopQueues.reduce(0) { $0 + $1.value.count } - } - - var isEmpty: Bool { - self.count == 0 - } - - var generalPurposeCount: Int { - self.generalPurposeQueue.count - } - - func count(for eventLoop: EventLoop) -> Int { - self.withEventLoopQueueIfAvailable(for: eventLoop.id) { $0.count } ?? 0 - } - - func isEmpty(for eventLoop: EventLoop?) -> Bool { - if let eventLoop = eventLoop { - return self.withEventLoopQueueIfAvailable(for: eventLoop.id) { $0.isEmpty } ?? true - } - return self.generalPurposeQueue.isEmpty - } - - @discardableResult - mutating func push(_ request: Request) -> Request.ID { - if let eventLoop = request.requiredEventLoop { - self.withEventLoopQueue(for: eventLoop.id) { queue in - queue.append(request) - } - } else { - self.generalPurposeQueue.append(request) - } - return request.id - } - - mutating func popFirst(for eventLoop: EventLoop? = nil) -> Request? { - if let eventLoop = eventLoop { - return self.withEventLoopQueue(for: eventLoop.id) { queue in - queue.popFirst() - } - } else { - return self.generalPurposeQueue.popFirst() - } - } - - /// removes up to `max` requests from the queue for the given `eventLoop` and returns them. - /// - Parameters: - /// - max: maximum number of requests to pop - /// - eventLoop: required event loop of the request - /// - Returns: requests for the given `eventLoop` - mutating func popFirst(max: Int, for eventLoop: EventLoop? = nil) -> [Request] { - if let eventLoop = eventLoop { - return self.withEventLoopQueue(for: eventLoop.id) { queue in - queue.popFirst(max: max) - } - } else { - return self.generalPurposeQueue.popFirst(max: max) - } - } - - mutating func remove(_ requestID: Request.ID) -> Request? { - if let eventLoopID = requestID.eventLoopID { - return self.withEventLoopQueue(for: eventLoopID) { queue in - guard let index = queue.firstIndex(where: { $0.id == requestID }) else { - return nil - } - return queue.remove(at: index) - } - } else { - if let index = self.generalPurposeQueue.firstIndex(where: { $0.id == requestID }) { - // TBD: This is slow. Do we maybe want something more sophisticated here? - return self.generalPurposeQueue.remove(at: index) - } - return nil - } - } - - mutating func removeAll() -> [Request] { - var result = [Request]() - result = self.eventLoopQueues.flatMap { $0.value } - result.append(contentsOf: self.generalPurposeQueue) - - self.eventLoopQueues.removeAll() - self.generalPurposeQueue.removeAll() - return result - } - - private mutating func withEventLoopQueue( - for eventLoopID: EventLoopID, - _ closure: (inout CircularBuffer) -> Result - ) -> Result { - if self.eventLoopQueues[eventLoopID] == nil { - self.eventLoopQueues[eventLoopID] = CircularBuffer(initialCapacity: 32) - } - return closure(&self.eventLoopQueues[eventLoopID]!) - } - - private func withEventLoopQueueIfAvailable( - for eventLoopID: EventLoopID, - _ closure: (CircularBuffer) -> Result - ) -> Result? { - if let queue = self.eventLoopQueues[eventLoopID] { - return closure(queue) - } - return nil - } - - /// - Returns: event loops with at least one request with a required event loop - func eventLoopsWithPendingRequests() -> [EventLoop] { - self.eventLoopQueues.compactMap { - /// all requests in `eventLoopQueues` are guaranteed to have a `requiredEventLoop` - /// however, a queue can be empty - $0.value.first?.requiredEventLoop! - } - } - - /// - Returns: request count for requests with required event loop, grouped by required event loop without any particular order - func requestCountGroupedByRequiredEventLoop() -> [(EventLoop, Int)] { - self.eventLoopQueues.values.compactMap { requests -> (EventLoop, Int)? in - /// all requests in `eventLoopQueues` are guaranteed to have a `requiredEventLoop`, - /// however, a queue can be empty - guard let requiredEventLoop = requests.first?.requiredEventLoop! else { - return nil - } - return (requiredEventLoop, requests.count) - } - } - - /// - Returns: request count with **no** required event loop, grouped by preferred event loop and ordered descending by number of requests - func generalPurposeRequestCountGroupedByPreferredEventLoop() -> [(EventLoop, Int)] { - let requestCountPerEventLoop = Dictionary( - self.generalPurposeQueue.lazy.map { request in - (HashableEventLoop(request.preferredEventLoop), 1) - }, - uniquingKeysWith: + - ) - return requestCountPerEventLoop.lazy - .map { ($0.key.eventLoop, $0.value) } - .sorted { lhs, rhs in - lhs.1 > rhs.1 - } - } - } -} - -extension CircularBuffer { - /// Removes up to `max` elements from the beginning of the - /// `CircularBuffer` and returns them. - /// - /// Calling this method may invalidate any existing indices for use with this - /// `CircularBuffer`. - /// - /// - Parameter max: The number of elements to remove. - /// `max` must be greater than or equal to zero. - /// - Returns: removed elements - /// - /// - Complexity: O(*k*), where *k* is the number of elements removed. - fileprivate mutating func popFirst(max: Int) -> [Element] { - precondition(max >= 0) - let elementCountToRemove = Swift.min(max, self.count) - let array = Array(self[self.startIndex..( - http1: (inout HTTP1StateMachine) -> ReturnValue, - http2: (inout HTTP2StateMachine) -> ReturnValue - ) -> ReturnValue { - let returnValue: ReturnValue - switch self { - case .http1(var http1State): - returnValue = http1(&http1State) - self = .http1(http1State) - case .http2(var http2State): - returnValue = http2(&http2State) - self = .http2(http2State) - } - return returnValue - } - } - - var state: HTTPVersionState - - let idGenerator: Connection.ID.Generator - let maximumConcurrentHTTP1Connections: Int - /// The property was introduced to fail fast during testing. - /// Otherwise this should always be true and not turned off. - private let retryConnectionEstablishment: Bool - let maximumConnectionUses: Int? - - init( - idGenerator: Connection.ID.Generator, - maximumConcurrentHTTP1Connections: Int, - retryConnectionEstablishment: Bool, - preferHTTP1: Bool, - maximumConnectionUses: Int? - ) { - self.maximumConcurrentHTTP1Connections = maximumConcurrentHTTP1Connections - self.retryConnectionEstablishment = retryConnectionEstablishment - self.idGenerator = idGenerator - self.maximumConnectionUses = maximumConnectionUses - - if preferHTTP1 { - let http1State = HTTP1StateMachine( - idGenerator: idGenerator, - maximumConcurrentConnections: maximumConcurrentHTTP1Connections, - retryConnectionEstablishment: retryConnectionEstablishment, - maximumConnectionUses: maximumConnectionUses, - lifecycleState: .running - ) - self.state = .http1(http1State) - } else { - let http2State = HTTP2StateMachine( - idGenerator: idGenerator, - retryConnectionEstablishment: retryConnectionEstablishment, - lifecycleState: .running, - maximumConnectionUses: maximumConnectionUses - ) - self.state = .http2(http2State) - } - } - - mutating func executeRequest(_ request: Request) -> Action { - self.state.modify( - http1: { http1 in - http1.executeRequest(request) - }, - http2: { http2 in - http2.executeRequest(request) - } - ) - } - - mutating func newHTTP1ConnectionCreated(_ connection: Connection) -> Action { - switch self.state { - case .http1(var http1StateMachine): - let action = http1StateMachine.newHTTP1ConnectionEstablished(connection) - self.state = .http1(http1StateMachine) - return action - - case .http2(let http2StateMachine): - var http1StateMachine = HTTP1StateMachine( - idGenerator: self.idGenerator, - maximumConcurrentConnections: self.maximumConcurrentHTTP1Connections, - retryConnectionEstablishment: self.retryConnectionEstablishment, - maximumConnectionUses: self.maximumConnectionUses, - lifecycleState: http2StateMachine.lifecycleState - ) - - let newConnectionAction = http1StateMachine.migrateFromHTTP2( - http1Connections: http2StateMachine.http1Connections, - http2Connections: http2StateMachine.connections, - requests: http2StateMachine.requests, - newHTTP1Connection: connection - ) - self.state = .http1(http1StateMachine) - return newConnectionAction - } - } - - mutating func newHTTP2ConnectionCreated(_ connection: Connection, maxConcurrentStreams: Int) -> Action { - switch self.state { - case .http1(let http1StateMachine): - - var http2StateMachine = HTTP2StateMachine( - idGenerator: self.idGenerator, - retryConnectionEstablishment: self.retryConnectionEstablishment, - lifecycleState: http1StateMachine.lifecycleState, - maximumConnectionUses: self.maximumConnectionUses - ) - let migrationAction = http2StateMachine.migrateFromHTTP1( - http1Connections: http1StateMachine.connections, - http2Connections: http1StateMachine.http2Connections, - requests: http1StateMachine.requests, - newHTTP2Connection: connection, - maxConcurrentStreams: maxConcurrentStreams - ) - - self.state = .http2(http2StateMachine) - return migrationAction - - case .http2(var http2StateMachine): - let newConnectionAction = http2StateMachine.newHTTP2ConnectionEstablished( - connection, - maxConcurrentStreams: maxConcurrentStreams - ) - self.state = .http2(http2StateMachine) - return newConnectionAction - } - } - - mutating func newHTTP2MaxConcurrentStreamsReceived(_ connectionID: Connection.ID, newMaxStreams: Int) -> Action - { - self.state.modify( - http1: { http1 in - http1.newHTTP2MaxConcurrentStreamsReceived(connectionID, newMaxStreams: newMaxStreams) - }, - http2: { http2 in - http2.newHTTP2MaxConcurrentStreamsReceived(connectionID, newMaxStreams: newMaxStreams) - } - ) - } - - mutating func http2ConnectionGoAwayReceived(_ connectionID: Connection.ID) -> Action { - self.state.modify( - http1: { http1 in - http1.http2ConnectionGoAwayReceived(connectionID) - }, - http2: { http2 in - http2.http2ConnectionGoAwayReceived(connectionID) - } - ) - } - - mutating func http2ConnectionClosed(_ connectionID: Connection.ID) -> Action { - self.state.modify( - http1: { http1 in - http1.http2ConnectionClosed(connectionID) - }, - http2: { http2 in - http2.http2ConnectionClosed(connectionID) - } - ) - } - - mutating func http2ConnectionStreamClosed(_ connectionID: Connection.ID) -> Action { - self.state.modify( - http1: { http1 in - http1.http2ConnectionStreamClosed(connectionID) - }, - http2: { http2 in - http2.http2ConnectionStreamClosed(connectionID) - } - ) - } - - mutating func failedToCreateNewConnection(_ error: Error, connectionID: Connection.ID) -> Action { - self.state.modify( - http1: { http1 in - http1.failedToCreateNewConnection(error, connectionID: connectionID) - }, - http2: { http2 in - http2.failedToCreateNewConnection(error, connectionID: connectionID) - } - ) - } - - mutating func waitingForConnectivity(_ error: Error, connectionID: Connection.ID) -> Action { - self.state.modify( - http1: { http1 in - http1.waitingForConnectivity(error, connectionID: connectionID) - }, - http2: { http2 in - http2.waitingForConnectivity(error, connectionID: connectionID) - } - ) - } - - mutating func connectionCreationBackoffDone(_ connectionID: Connection.ID) -> Action { - self.state.modify( - http1: { http1 in - http1.connectionCreationBackoffDone(connectionID) - }, - http2: { http2 in - http2.connectionCreationBackoffDone(connectionID) - } - ) - } - - /// A request has timed out. - /// - /// This is different to a request being cancelled. If a request times out, we need to fail the - /// request, but don't need to cancel the timer (it already triggered). If a request is cancelled - /// we don't need to fail it but we need to cancel its timeout timer. - mutating func timeoutRequest(_ requestID: Request.ID) -> Action { - self.state.modify( - http1: { http1 in - http1.timeoutRequest(requestID) - }, - http2: { http2 in - http2.timeoutRequest(requestID) - } - ) - } - - /// A request was cancelled. - /// - /// This is different to a request timing out. If a request is cancelled we don't need to fail it but we - /// need to cancel its timeout timer. If a request times out, we need to fail the request, but don't - /// need to cancel the timer (it already triggered). - mutating func cancelRequest(_ requestID: Request.ID) -> Action { - self.state.modify( - http1: { http1 in - http1.cancelRequest(requestID) - }, - http2: { http2 in - http2.cancelRequest(requestID) - } - ) - } - - mutating func connectionIdleTimeout(_ connectionID: Connection.ID) -> Action { - self.state.modify( - http1: { http1 in - http1.connectionIdleTimeout(connectionID) - }, - http2: { http2 in - http2.connectionIdleTimeout(connectionID) - } - ) - } - - /// A connection has been closed - mutating func http1ConnectionClosed(_ connectionID: Connection.ID) -> Action { - self.state.modify( - http1: { http1 in - http1.http1ConnectionClosed(connectionID) - }, - http2: { http2 in - http2.http1ConnectionClosed(connectionID) - } - ) - } - - mutating func http1ConnectionReleased(_ connectionID: Connection.ID) -> Action { - self.state.modify( - http1: { http1 in - http1.http1ConnectionReleased(connectionID) - }, - http2: { http2 in - http2.http1ConnectionReleased(connectionID) - } - ) - } - - mutating func shutdown() -> Action { - self.state.modify( - http1: { http1 in - http1.shutdown() - }, - http2: { http2 in - http2.shutdown() - } - ) - } - } -} - -extension HTTPConnectionPool { - /// The pool cleanup todo list. - struct CleanupContext: Equatable { - /// the connections to close right away. These are idle. - var close: [Connection] - - /// the connections that currently run a request that needs to be cancelled to close the connections - var cancel: [Connection] - - /// the connections that are backing off from connection creation - var connectBackoff: [Connection.ID] - - init(close: [Connection] = [], cancel: [Connection] = [], connectBackoff: [Connection.ID] = []) { - self.close = close - self.cancel = cancel - self.connectBackoff = connectBackoff - } - } -} - -extension HTTPConnectionPool.StateMachine: CustomStringConvertible { - var description: String { - switch self.state { - case .http1(let http1): - return ".http1(\(http1))" - case .http2(let http2): - return ".http2(\(http2))" - } - } -} - -extension HTTPConnectionPool.StateMachine { - struct ConnectionMigrationAction { - var closeConnections: [HTTPConnectionPool.Connection] - var createConnections: [(HTTPConnectionPool.Connection.ID, EventLoop)] - } - - struct EstablishedAction { - static var none: Self { - Self(request: .none, connection: .none) - } - let request: HTTPConnectionPool.StateMachine.RequestAction - let connection: EstablishedConnectionAction - } - - enum EstablishedConnectionAction { - case none - case scheduleTimeoutTimer(HTTPConnectionPool.Connection.ID, on: EventLoop) - case closeConnection( - HTTPConnectionPool.Connection, - isShutdown: HTTPConnectionPool.StateMachine.ConnectionAction.IsShutdown - ) - } -} - -extension HTTPConnectionPool.StateMachine.Action { - init(_ action: HTTPConnectionPool.StateMachine.EstablishedAction) { - self.init( - request: action.request, - connection: .init(action.connection) - ) - } -} - -extension HTTPConnectionPool.StateMachine.ConnectionAction { - init(_ action: HTTPConnectionPool.StateMachine.EstablishedConnectionAction) { - switch action { - case .none: - self = .none - case .scheduleTimeoutTimer(let connectionID, let eventLoop): - self = .scheduleTimeoutTimer(connectionID, on: eventLoop) - case .closeConnection(let connection, let isShutdown): - self = .closeConnection(connection, isShutdown: isShutdown) - } - } -} - -extension HTTPConnectionPool.StateMachine.ConnectionAction { - static func combined( - _ migrationAction: HTTPConnectionPool.StateMachine.ConnectionMigrationAction, - _ establishedAction: HTTPConnectionPool.StateMachine.EstablishedConnectionAction - ) -> Self { - switch establishedAction { - case .none: - return .migration( - createConnections: migrationAction.createConnections, - closeConnections: migrationAction.closeConnections, - scheduleTimeout: nil - ) - case .closeConnection(let connection, let isShutdown): - guard isShutdown == .no else { - precondition( - migrationAction.closeConnections.isEmpty && migrationAction.createConnections.isEmpty, - "migration actions are not supported during shutdown" - ) - return .closeConnection(connection, isShutdown: isShutdown) - } - var closeConnections = migrationAction.closeConnections - closeConnections.append(connection) - return .migration( - createConnections: migrationAction.createConnections, - closeConnections: closeConnections, - scheduleTimeout: nil - ) - case .scheduleTimeoutTimer(let connectionID, let eventLoop): - return .migration( - createConnections: migrationAction.createConnections, - closeConnections: migrationAction.closeConnections, - scheduleTimeout: (connectionID, eventLoop) - ) - } - } -} diff --git a/Sources/AsyncHTTPClient/ConnectionTarget.swift b/Sources/AsyncHTTPClient/ConnectionTarget.swift deleted file mode 100644 index 207a6e1bb..000000000 --- a/Sources/AsyncHTTPClient/ConnectionTarget.swift +++ /dev/null @@ -1,64 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2019-2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import enum NIOCore.SocketAddress - -enum ConnectionTarget: Equatable, Hashable { - // We keep the IP address serialization precisely as it is in the URL. - // Some platforms have quirks in their implementations of 'ntop', for example - // writing IPv6 addresses as having embedded IPv4 sections (e.g. [::192.168.0.1] vs [::c0a8:1]). - // This serialization includes square brackets, so it is safe to write next to a port number. - // Note: `address` must have an explicit port. - case ipAddress(serialization: String, address: SocketAddress) - case domain(name: String, port: Int) - case unixSocket(path: String) - - init(remoteHost: String, port: Int) { - if let addr = try? SocketAddress(ipAddress: remoteHost, port: port) { - switch addr { - case .v6: - self = .ipAddress(serialization: "[\(remoteHost)]", address: addr) - case .v4: - self = .ipAddress(serialization: remoteHost, address: addr) - case .unixDomainSocket: - fatalError("Expected a remote host") - } - } else { - precondition(!remoteHost.isEmpty, "HTTPClient.Request should already reject empty remote hostnames") - self = .domain(name: remoteHost, port: port) - } - } -} - -extension ConnectionTarget { - /// The host name which will be send as an HTTP `Host` header. - /// Only returns nil if the `self` is a `unixSocket`. - var host: String? { - switch self { - case .ipAddress(let serialization, _): return serialization - case .domain(let name, _): return name - case .unixSocket: return nil - } - } - - /// The host name which will be send as an HTTP host header. - /// Only returns nil if the `self` is a `unixSocket`. - var port: Int? { - switch self { - case .ipAddress(_, let address): return address.port! - case .domain(_, let port): return port - case .unixSocket: return nil - } - } -} diff --git a/Sources/AsyncHTTPClient/DeconstructedURL.swift b/Sources/AsyncHTTPClient/DeconstructedURL.swift deleted file mode 100644 index 52042bce3..000000000 --- a/Sources/AsyncHTTPClient/DeconstructedURL.swift +++ /dev/null @@ -1,113 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import struct Foundation.URL - -struct DeconstructedURL { - var scheme: Scheme - var connectionTarget: ConnectionTarget - var uri: String - - init( - scheme: Scheme, - connectionTarget: ConnectionTarget, - uri: String - ) { - self.scheme = scheme - self.connectionTarget = connectionTarget - self.uri = uri - } -} - -extension DeconstructedURL { - init(url: String) throws { - guard let url = URL(string: url) else { - throw HTTPClientError.invalidURL - } - try self.init(url: url) - } - - init(url: URL) throws { - guard let schemeString = url.scheme else { - throw HTTPClientError.emptyScheme - } - guard let scheme = Scheme(rawValue: schemeString.lowercased()) else { - throw HTTPClientError.unsupportedScheme(schemeString) - } - - switch scheme { - case .http, .https: - #if !canImport(Darwin) && compiler(>=6.0) - guard let urlHost = url.host, !urlHost.isEmpty else { - throw HTTPClientError.emptyHost - } - let host = urlHost.trimIPv6Brackets() - #else - guard let host = url.host, !host.isEmpty else { - throw HTTPClientError.emptyHost - } - #endif - self.init( - scheme: scheme, - connectionTarget: .init(remoteHost: host, port: url.port ?? scheme.defaultPort), - uri: url.uri - ) - - case .httpUnix, .httpsUnix: - guard let socketPath = url.host, !socketPath.isEmpty else { - throw HTTPClientError.missingSocketPath - } - self.init( - scheme: scheme, - connectionTarget: .unixSocket(path: socketPath), - uri: url.uri - ) - - case .unix: - let socketPath = url.baseURL?.path ?? url.path - let uri = url.baseURL != nil ? url.uri : "/" - guard !socketPath.isEmpty else { - throw HTTPClientError.missingSocketPath - } - self.init( - scheme: scheme, - connectionTarget: .unixSocket(path: socketPath), - uri: uri - ) - } - } -} - -#if !canImport(Darwin) && compiler(>=6.0) -extension String { - @inlinable internal func trimIPv6Brackets() -> String { - var utf8View = self.utf8[...] - - var modified = false - if utf8View.first == UInt8(ascii: "[") { - utf8View = utf8View.dropFirst() - modified = true - } - if utf8View.last == UInt8(ascii: "]") { - utf8View = utf8View.dropLast() - modified = true - } - - if modified { - return String(Substring(utf8View)) - } - return self - } -} -#endif diff --git a/Sources/AsyncHTTPClient/Docs.docc/index.md b/Sources/AsyncHTTPClient/Docs.docc/index.md deleted file mode 100644 index 37033e043..000000000 --- a/Sources/AsyncHTTPClient/Docs.docc/index.md +++ /dev/null @@ -1,325 +0,0 @@ -# ``AsyncHTTPClient`` - -This package provides simple HTTP Client library built on top of SwiftNIO. - -## Overview - -This library provides the following: -- First class support for Swift Concurrency (since version 1.9.0) -- Asynchronous and non-blocking request methods -- Simple follow-redirects (cookie headers are dropped) -- Streaming body download -- TLS support -- Automatic HTTP/2 over HTTPS (since version 1.7.0) -- Cookie parsing (but not storage) - -### Getting Started - -#### Adding the dependency - -Add the following entry in your Package.swift to start using HTTPClient: - -```swift -.package(url: "https://github.com/swift-server/async-http-client.git", from: "1.9.0") -``` -and `AsyncHTTPClient` dependency to your target: -```swift -.target(name: "MyApp", dependencies: [.product(name: "AsyncHTTPClient", package: "async-http-client")]), -``` - -#### Request-Response API - -The code snippet below illustrates how to make a simple GET request to a remote server. - -```swift -import AsyncHTTPClient - -/// MARK: - Using Swift Concurrency -let request = HTTPClientRequest(url: "https://apple.com/") -let response = try await httpClient.execute(request, timeout: .seconds(30)) -print("HTTP head", response) -if response.status == .ok { - let body = try await response.body.collect(upTo: 1024 * 1024) // 1 MB - // handle body -} else { - // handle remote error -} - - -/// MARK: - Using SwiftNIO EventLoopFuture -HTTPClient.shared.get(url: "https://apple.com/").whenComplete { result in - switch result { - case .failure(let error): - // process error - case .success(let response): - if response.status == .ok { - // handle response - } else { - // handle remote error - } - } -} -``` - -You should always shut down ``HTTPClient`` instances you created using ``HTTPClient/shutdown()-9gcpw``. Please note that you must not call ``HTTPClient/shutdown()-9gcpw`` before all requests of the HTTP client have finished, or else the in-flight requests will likely fail because their network connections are interrupted. - -#### async/await examples - -Examples for the async/await API can be found in the [`Examples` folder](https://github.com/swift-server/async-http-client/tree/main/Examples) in the repository. - -### Usage guide - -The default HTTP Method is `GET`. In case you need to have more control over the method, or you want to add headers or body, use the ``HTTPClientRequest`` struct: - -#### Using Swift Concurrency - -```swift -import AsyncHTTPClient - -do { - var request = HTTPClientRequest(url: "https://apple.com/") - request.method = .POST - request.headers.add(name: "User-Agent", value: "Swift HTTPClient") - request.body = .bytes(ByteBuffer(string: "some data")) - - let response = try await HTTPClient.shared.execute(request, timeout: .seconds(30)) - if response.status == .ok { - // handle response - } else { - // handle remote error - } -} catch { - // handle error -} -``` - -#### Using SwiftNIO EventLoopFuture - -```swift -import AsyncHTTPClient - -var request = try HTTPClient.Request(url: "https://apple.com/", method: .POST) -request.headers.add(name: "User-Agent", value: "Swift HTTPClient") -request.body = .string("some-body") - -HTTPClient.shared.execute(request: request).whenComplete { result in - switch result { - case .failure(let error): - // process error - case .success(let response): - if response.status == .ok { - // handle response - } else { - // handle remote error - } - } -} -``` - -#### Redirects following -Enable follow-redirects behavior using the client configuration: -```swift -let httpClient = HTTPClient(eventLoopGroupProvider: .singleton, - configuration: HTTPClient.Configuration(followRedirects: true)) -``` - -#### Timeouts -Timeouts (connect and read) can also be set using the client configuration: -```swift -let timeout = HTTPClient.Configuration.Timeout(connect: .seconds(1), read: .seconds(1)) -let httpClient = HTTPClient(eventLoopGroupProvider: .singleton, - configuration: HTTPClient.Configuration(timeout: timeout)) -``` -or on a per-request basis: -```swift -httpClient.execute(request: request, deadline: .now() + .milliseconds(1)) -``` - -#### Streaming -When dealing with larger amount of data, it's critical to stream the response body instead of aggregating in-memory. -The following example demonstrates how to count the number of bytes in a streaming response body: - -##### Using Swift Concurrency -```swift -do { - let request = HTTPClientRequest(url: "https://apple.com/") - let response = try await HTTPClient.shared.execute(request, timeout: .seconds(30)) - print("HTTP head", response) - - // if defined, the content-length headers announces the size of the body - let expectedBytes = response.headers.first(name: "content-length").flatMap(Int.init) - - var receivedBytes = 0 - // asynchronously iterates over all body fragments - // this loop will automatically propagate backpressure correctly - for try await buffer in response.body { - // for this example, we are just interested in the size of the fragment - receivedBytes += buffer.readableBytes - - if let expectedBytes = expectedBytes { - // if the body size is known, we calculate a progress indicator - let progress = Double(receivedBytes) / Double(expectedBytes) - print("progress: \(Int(progress * 100))%") - } - } - print("did receive \(receivedBytes) bytes") -} catch { - print("request failed:", error) -} -``` - -##### Using HTTPClientResponseDelegate and SwiftNIO EventLoopFuture - -```swift -import NIOCore -import NIOHTTP1 - -class CountingDelegate: HTTPClientResponseDelegate { - typealias Response = Int - - var count = 0 - - func didSendRequestHead(task: HTTPClient.Task, _ head: HTTPRequestHead) { - // this is executed right after request head was sent, called once - } - - func didSendRequestPart(task: HTTPClient.Task, _ part: IOData) { - // this is executed when request body part is sent, could be called zero or more times - } - - func didSendRequest(task: HTTPClient.Task) { - // this is executed when request is fully sent, called once - } - - func didReceiveHead( - task: HTTPClient.Task, - _ head: HTTPResponseHead - ) -> EventLoopFuture { - // this is executed when we receive HTTP response head part of the request - // (it contains response code and headers), called once in case backpressure - // is needed, all reads will be paused until returned future is resolved - return task.eventLoop.makeSucceededFuture(()) - } - - func didReceiveBodyPart( - task: HTTPClient.Task, - _ buffer: ByteBuffer - ) -> EventLoopFuture { - // this is executed when we receive parts of the response body, could be called zero or more times - count += buffer.readableBytes - // in case backpressure is needed, all reads will be paused until returned future is resolved - return task.eventLoop.makeSucceededFuture(()) - } - - func didFinishRequest(task: HTTPClient.Task) throws -> Int { - // this is called when the request is fully read, called once - // this is where you return a result or throw any errors you require to propagate to the client - return count - } - - func didReceiveError(task: HTTPClient.Task, _ error: Error) { - // this is called when we receive any network-related error, called once - } -} - -let request = try HTTPClient.Request(url: "https://apple.com/") -let delegate = CountingDelegate() - -httpClient.execute(request: request, delegate: delegate).futureResult.whenSuccess { count in - print(count) -} -``` - -#### File downloads - -Based on the `HTTPClientResponseDelegate` example above you can build more complex delegates, -the built-in `FileDownloadDelegate` is one of them. It allows streaming the downloaded data -asynchronously, while reporting the download progress at the same time, like in the following -example: - -```swift -let request = try HTTPClient.Request( - url: "https://swift.org/builds/development/ubuntu1804/latest-build.yml" -) - -let delegate = try FileDownloadDelegate(path: "/tmp/latest-build.yml", reportProgress: { - if let totalBytes = $0.totalBytes { - print("Total bytes count: \(totalBytes)") - } - print("Downloaded \($0.receivedBytes) bytes so far") -}) - -HTTPClient.shared.execute(request: request, delegate: delegate).futureResult - .whenSuccess { progress in - if let totalBytes = progress.totalBytes { - print("Final total bytes count: \(totalBytes)") - } - print("Downloaded finished with \(progress.receivedBytes) bytes downloaded") - } -``` - -#### Unix Domain Socket Paths -Connecting to servers bound to socket paths is easy: -```swift -HTTPClient.shared.execute( - .GET, - socketPath: "/tmp/myServer.socket", - urlPath: "/path/to/resource" -).whenComplete (...) -``` - -Connecting over TLS to a unix domain socket path is possible as well: -```swift -HTTPClient.shared.execute( - .POST, - secureSocketPath: "/tmp/myServer.socket", - urlPath: "/path/to/resource", - body: .string("hello") -).whenComplete (...) -``` - -Direct URLs can easily be constructed to be executed in other scenarios: -```swift -let socketPathBasedURL = URL( - httpURLWithSocketPath: "/tmp/myServer.socket", - uri: "/path/to/resource" -) -let secureSocketPathBasedURL = URL( - httpsURLWithSocketPath: "/tmp/myServer.socket", - uri: "/path/to/resource" -) -``` - -#### Disabling HTTP/2 -The exclusive use of HTTP/1 is possible by setting ``HTTPClient/Configuration/httpVersion-swift.property`` to ``HTTPClient/Configuration/HTTPVersion-swift.struct/http1Only`` on the ``HTTPClient/Configuration``: -```swift -var configuration = HTTPClient.Configuration() -configuration.httpVersion = .http1Only -let client = HTTPClient( - eventLoopGroupProvider: .singleton, - configuration: configuration -) -``` - -### Security - -AsyncHTTPClient's security process is documented on [GitHub](https://github.com/swift-server/async-http-client/blob/main/SECURITY.md). - -## Topics - -### HTTPClient - -- ``HTTPClient`` -- ``HTTPClientRequest`` -- ``HTTPClientResponse`` - -### HTTP Client Delegates - -- ``HTTPClientResponseDelegate`` -- ``ResponseAccumulator`` -- ``FileDownloadDelegate`` -- ``HTTPClientCopyingDelegate`` - -### Errors - -- ``HTTPClientError`` diff --git a/Sources/AsyncHTTPClient/FileDownloadDelegate.swift b/Sources/AsyncHTTPClient/FileDownloadDelegate.swift deleted file mode 100644 index 33a4d3cb2..000000000 --- a/Sources/AsyncHTTPClient/FileDownloadDelegate.swift +++ /dev/null @@ -1,290 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2020 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOConcurrencyHelpers -import NIOCore -import NIOHTTP1 -import NIOPosix - -import struct Foundation.URL - -/// Handles a streaming download to a given file path, allowing headers and progress to be reported. -public final class FileDownloadDelegate: HTTPClientResponseDelegate { - /// The response type for this delegate: the total count of bytes as reported by the response - /// "Content-Length" header (if available), the count of bytes downloaded, the - /// response head, and a history of requests and responses. - public struct Progress: Sendable { - public var totalBytes: Int? - public var receivedBytes: Int - - /// The history of all requests and responses in redirect order. - public var history: [HTTPClient.RequestResponse] = [] - - /// The target URL (after redirects) of the response. - public var url: URL? { - self.history.last?.request.url - } - - public var head: HTTPResponseHead { - get { - assert(self._head != nil) - return self._head! - } - set { - self._head = newValue - } - } - - fileprivate var _head: HTTPResponseHead? = nil - - internal init(totalBytes: Int? = nil, receivedBytes: Int) { - self.totalBytes = totalBytes - self.receivedBytes = receivedBytes - } - } - - private struct State { - var progress = Progress( - totalBytes: nil, - receivedBytes: 0 - ) - var fileIOThreadPool: NIOThreadPool? - var fileHandleFuture: EventLoopFuture? - var writeFuture: EventLoopFuture? - } - private let state: NIOLockedValueBox - - var _fileIOThreadPool: NIOThreadPool? { - self.state.withLockedValue { $0.fileIOThreadPool } - } - - public typealias Response = Progress - - private let filePath: String - private let reportHead: (@Sendable (HTTPClient.Task, HTTPResponseHead) -> Void)? - private let reportProgress: (@Sendable (HTTPClient.Task, Progress) -> Void)? - - /// Initializes a new file download delegate. - /// - /// - parameters: - /// - path: Path to a file you'd like to write the download to. - /// - pool: A thread pool to use for asynchronous file I/O. If nil, a shared thread pool will be used. Defaults to nil. - /// - reportHead: A closure called when the response head is available. - /// - reportProgress: A closure called when a body chunk has been downloaded, with - /// the total byte count and download byte count passed to it as arguments. The callbacks - /// will be invoked in the same threading context that the delegate itself is invoked, - /// as controlled by `EventLoopPreference`. - @preconcurrency - public init( - path: String, - pool: NIOThreadPool? = nil, - reportHead: (@Sendable (HTTPClient.Task, HTTPResponseHead) -> Void)? = nil, - reportProgress: (@Sendable (HTTPClient.Task, Progress) -> Void)? = nil - ) throws { - self.state = NIOLockedValueBox(State(fileIOThreadPool: pool)) - self.filePath = path - - self.reportHead = reportHead - self.reportProgress = reportProgress - } - - /// Initializes a new file download delegate. - /// - /// - parameters: - /// - path: Path to a file you'd like to write the download to. - /// - pool: A thread pool to use for asynchronous file I/O. - /// - reportHead: A closure called when the response head is available. - /// - reportProgress: A closure called when a body chunk has been downloaded, with - /// the total byte count and download byte count passed to it as arguments. The callbacks - /// will be invoked in the same threading context that the delegate itself is invoked, - /// as controlled by `EventLoopPreference`. - @preconcurrency - public convenience init( - path: String, - pool: NIOThreadPool, - reportHead: (@Sendable (HTTPResponseHead) -> Void)? = nil, - reportProgress: (@Sendable (Progress) -> Void)? = nil - ) throws { - try self.init( - path: path, - pool: .some(pool), - reportHead: reportHead.map { reportHead in - { @Sendable _, head in - reportHead(head) - } - }, - reportProgress: reportProgress.map { reportProgress in - { @Sendable _, head in - reportProgress(head) - } - } - ) - } - - /// Initializes a new file download delegate and uses the shared thread pool of the ``HTTPClient`` for file I/O. - /// - /// - parameters: - /// - path: Path to a file you'd like to write the download to. - /// - reportHead: A closure called when the response head is available. - /// - reportProgress: A closure called when a body chunk has been downloaded, with - /// the total byte count and download byte count passed to it as arguments. The callbacks - /// will be invoked in the same threading context that the delegate itself is invoked, - /// as controlled by `EventLoopPreference`. - @preconcurrency - public convenience init( - path: String, - reportHead: (@Sendable (HTTPResponseHead) -> Void)? = nil, - reportProgress: (@Sendable (Progress) -> Void)? = nil - ) throws { - try self.init( - path: path, - pool: nil, - reportHead: reportHead.map { reportHead in - { @Sendable _, head in - reportHead(head) - } - }, - reportProgress: reportProgress.map { reportProgress in - { @Sendable _, head in - reportProgress(head) - } - } - ) - } - - public func didVisitURL(task: HTTPClient.Task, _ request: HTTPClient.Request, _ head: HTTPResponseHead) { - self.state.withLockedValue { - $0.progress.history.append(.init(request: request, responseHead: head)) - } - } - - public func didReceiveHead( - task: HTTPClient.Task, - _ head: HTTPResponseHead - ) -> EventLoopFuture { - self.state.withLockedValue { - $0.progress._head = head - - if let totalBytesString = head.headers.first(name: "Content-Length"), - let totalBytes = Int(totalBytesString) - { - $0.progress.totalBytes = totalBytes - } - } - - self.reportHead?(task, head) - - return task.eventLoop.makeSucceededFuture(()) - } - - public func didReceiveBodyPart( - task: HTTPClient.Task, - _ buffer: ByteBuffer - ) -> EventLoopFuture { - let (progress, io) = self.state.withLockedValue { state in - let threadPool: NIOThreadPool = { - guard let pool = state.fileIOThreadPool else { - let pool = task.fileIOThreadPool - state.fileIOThreadPool = pool - return pool - } - return pool - }() - - let io = NonBlockingFileIO(threadPool: threadPool) - state.progress.receivedBytes += buffer.readableBytes - return (state.progress, io) - } - self.reportProgress?(task, progress) - - let writeFuture = self.state.withLockedValue { state in - let writeFuture: EventLoopFuture - if let fileHandleFuture = state.fileHandleFuture { - writeFuture = fileHandleFuture.flatMap { - io.write(fileHandle: $0, buffer: buffer, eventLoop: task.eventLoop) - } - } else { - let fileHandleFuture = io.openFile( - _deprecatedPath: self.filePath, - mode: .write, - flags: .allowFileCreation(), - eventLoop: task.eventLoop - ) - state.fileHandleFuture = fileHandleFuture - writeFuture = fileHandleFuture.flatMap { - io.write(fileHandle: $0, buffer: buffer, eventLoop: task.eventLoop) - } - } - - state.writeFuture = writeFuture - return writeFuture - } - - return writeFuture - } - - private func close(fileHandle: NIOFileHandle) { - try! fileHandle.close() - self.state.withLockedValue { - $0.fileHandleFuture = nil - } - } - - private func finalize() { - enum Finalize { - case writeFuture(EventLoopFuture) - case fileHandleFuture(EventLoopFuture) - case none - } - - let finalize: Finalize = self.state.withLockedValue { state in - if let writeFuture = state.writeFuture { - return .writeFuture(writeFuture) - } else if let fileHandleFuture = state.fileHandleFuture { - return .fileHandleFuture(fileHandleFuture) - } else { - return .none - } - } - - switch finalize { - case .writeFuture(let future): - future.whenComplete { _ in - let fileHandleFuture = self.state.withLockedValue { state in - let future = state.fileHandleFuture - state.fileHandleFuture = nil - state.writeFuture = nil - return future - } - - fileHandleFuture?.whenSuccess { - self.close(fileHandle: $0) - } - } - case .fileHandleFuture(let future): - future.whenSuccess { self.close(fileHandle: $0) } - case .none: - () - } - } - - public func didReceiveError(task: HTTPClient.Task, _ error: Error) { - self.finalize() - } - - public func didFinishRequest(task: HTTPClient.Task) throws -> Response { - self.finalize() - return self.state.withLockedValue { $0.progress } - } -} diff --git a/Sources/AsyncHTTPClient/FoundationExtensions.swift b/Sources/AsyncHTTPClient/FoundationExtensions.swift deleted file mode 100644 index 452cb7b13..000000000 --- a/Sources/AsyncHTTPClient/FoundationExtensions.swift +++ /dev/null @@ -1,75 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2018-2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -// Extensions which provide better ergonomics when using Foundation types, -// or by using Foundation APIs. - -import Foundation - -extension HTTPClient.Cookie { - /// The cookie's expiration date. - public var expires: Date? { - get { - expires_timestamp.map { Date(timeIntervalSince1970: TimeInterval($0)) } - } - set { - expires_timestamp = newValue.map { Int64($0.timeIntervalSince1970) } - } - } - - /// Create HTTP cookie. - /// - /// - parameters: - /// - name: The name of the cookie. - /// - value: The cookie's string value. - /// - path: The cookie's path. - /// - domain: The domain of the cookie, defaults to nil. - /// - expires: The cookie's expiration date, defaults to nil. - /// - maxAge: The cookie's age in seconds, defaults to nil. - /// - httpOnly: Whether this cookie should be used by HTTP servers only, defaults to false. - /// - secure: Whether this cookie should only be sent using secure channels, defaults to false. - public init( - name: String, - value: String, - path: String = "/", - domain: String? = nil, - expires: Date? = nil, - maxAge: Int? = nil, - httpOnly: Bool = false, - secure: Bool = false - ) { - // FIXME: This should be failable and validate the inputs - // (for example, checking that the strings are ASCII, path begins with "/", domain is not empty, etc). - self.init( - name: name, - value: value, - path: path, - domain: domain, - expires_timestamp: expires.map { Int64($0.timeIntervalSince1970) }, - maxAge: maxAge, - httpOnly: httpOnly, - secure: secure - ) - } -} - -extension HTTPClient.Body { - /// Create and stream body using `Data`. - /// - /// - parameters: - /// - data: Body `Data` representation. - public static func data(_ data: Data) -> HTTPClient.Body { - self.bytes(data) - } -} diff --git a/Sources/AsyncHTTPClient/HTTPClient+HTTPCookie.swift b/Sources/AsyncHTTPClient/HTTPClient+HTTPCookie.swift deleted file mode 100644 index 759f6728a..000000000 --- a/Sources/AsyncHTTPClient/HTTPClient+HTTPCookie.swift +++ /dev/null @@ -1,256 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2018-2019 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import CAsyncHTTPClient -import NIOCore -import NIOHTTP1 - -#if canImport(xlocale) -import xlocale -#elseif canImport(locale_h) -import locale_h -#endif - -#if canImport(Darwin) -import Darwin -#elseif canImport(Musl) -import Musl -#elseif canImport(Android) -import Android -#elseif canImport(Glibc) -import Glibc -#endif - -extension HTTPClient { - /// A representation of an HTTP cookie. - public struct Cookie: Sendable { - /// The name of the cookie. - public var name: String - /// The cookie's string value. - public var value: String - /// The cookie's path. - public var path: String - /// The domain of the cookie. - public var domain: String? - /// The cookie's expiration date, as a number of seconds since the Unix epoch. - var expires_timestamp: Int64? - /// The cookie's age in seconds. - public var maxAge: Int? - /// Whether the cookie should only be sent to HTTP servers. - public var httpOnly: Bool - /// Whether the cookie should only be sent over secure channels. - public var secure: Bool - - /// Create a Cookie by parsing a `Set-Cookie` header. - /// - /// - parameters: - /// - header: String representation of the `Set-Cookie` response header. - /// - defaultDomain: Default domain to use if cookie was sent without one. - public init?(header: String, defaultDomain: String) { - // The parsing of "Set-Cookie" headers is defined by Section 5.2, RFC-6265: - // https://datatracker.ietf.org/doc/html/rfc6265#section-5.2 - var components = header.utf8.split(separator: UInt8(ascii: ";"), omittingEmptySubsequences: false)[...] - guard let keyValuePair = components.popFirst()?.trimmingASCIISpaces() else { - return nil - } - guard let (trimmedName, trimmedValue) = keyValuePair.parseKeyValuePair() else { - return nil - } - guard !trimmedName.isEmpty else { - return nil - } - - self.name = String(aligningUTF8: trimmedName) - self.value = String(aligningUTF8: trimmedValue.trimmingPairedASCIIQuote()) - self.expires_timestamp = nil - self.maxAge = nil - self.httpOnly = false - self.secure = false - - var parsedPath: String.UTF8View.SubSequence? - var parsedDomain: String.UTF8View.SubSequence? - - for component in components { - switch component.parseCookieComponent() { - case ("path", let value)?: - // Unlike other values, unspecified, empty, and invalid paths reset to the default path. - // https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.4 - guard let value = value, value.first == UInt8(ascii: "/") else { - parsedPath = nil - continue - } - parsedPath = value - case ("domain", let value)?: - guard var value = value, !value.isEmpty else { - continue - } - if value.first == UInt8(ascii: ".") { - value.removeFirst() - } - guard !value.isEmpty else { - parsedDomain = nil - continue - } - parsedDomain = value - case ("expires", let value)?: - guard let value = value, let timestamp = parseCookieTime(value) else { - continue - } - self.expires_timestamp = timestamp - case ("max-age", let value)?: - guard let value = value, let age = Int(Substring(value)) else { - continue - } - self.maxAge = age - case ("secure", _)?: - self.secure = true - case ("httponly", _)?: - self.httpOnly = true - default: - continue - } - } - - self.domain = parsedDomain.map { Substring($0).lowercased() } ?? defaultDomain.lowercased() - self.path = parsedPath.map { String(aligningUTF8: $0) } ?? "/" - } - - /// Create HTTP cookie. - /// - /// - parameters: - /// - name: The name of the cookie. - /// - value: The cookie's string value. - /// - path: The cookie's path. - /// - domain: The domain of the cookie, defaults to nil. - /// - expires_timestamp: The cookie's expiration date, as a number of seconds since the Unix epoch. defaults to nil. - /// - maxAge: The cookie's age in seconds, defaults to nil. - /// - httpOnly: Whether this cookie should be used by HTTP servers only, defaults to false. - /// - secure: Whether this cookie should only be sent using secure channels, defaults to false. - internal init( - name: String, - value: String, - path: String = "/", - domain: String? = nil, - expires_timestamp: Int64? = nil, - maxAge: Int? = nil, - httpOnly: Bool = false, - secure: Bool = false - ) { - self.name = name - self.value = value - self.path = path - self.domain = domain - self.expires_timestamp = expires_timestamp - self.maxAge = maxAge - self.httpOnly = httpOnly - self.secure = secure - } - } -} - -extension HTTPClient.Response { - /// List of HTTP cookies returned by the server. - public var cookies: [HTTPClient.Cookie] { - self.headers["set-cookie"].compactMap { HTTPClient.Cookie(header: $0, defaultDomain: self.host) } - } -} - -extension String { - /// Creates a String from a slice of UTF8 code-units, aligning the bounds to unicode scalar boundaries if needed. - fileprivate init(aligningUTF8 utf8Slice: String.UTF8View.SubSequence) { - self.init(Substring(utf8Slice)) - } -} - -extension String.UTF8View.SubSequence { - fileprivate func trimmingASCIISpaces() -> SubSequence { - guard let start = self.firstIndex(where: { $0 != UInt8(ascii: " ") }) else { - return self[self.endIndex.. SubSequence { - let quoteChar = UInt8(ascii: "\"") - var trimmed = self - if trimmed.popFirst() == quoteChar && trimmed.popLast() == quoteChar { - return trimmed - } - return self - } - - /// Splits this collection in to a key and value at the first ASCII '=' character. - /// Both the key and value are trimmed of ASCII spaces. - fileprivate func parseKeyValuePair() -> (key: SubSequence, value: SubSequence)? { - guard let keyValueSeparator = self.firstIndex(of: UInt8(ascii: "=")) else { - return nil - } - let trimmedName = self[.. (key: String, value: SubSequence?)? { - let (trimmedName, trimmedValue) = self.parseKeyValuePair() ?? (self.trimmingASCIISpaces(), nil) - guard !trimmedName.isEmpty else { - return nil - } - return (Substring(trimmedName).lowercased(), trimmedValue) - } -} - -nonisolated(unsafe) private let posixLocale: UnsafeMutableRawPointer = { - // All POSIX systems must provide a "POSIX" locale, and its date/time formats are US English. - // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html#tag_07_03_05 - let _posixLocale = newlocale(LC_TIME_MASK | LC_NUMERIC_MASK, "POSIX", nil)! - return UnsafeMutableRawPointer(_posixLocale) -}() - -private func parseTimestamp(_ utf8: String.UTF8View.SubSequence, format: String) -> tm? { - var timeComponents = tm() - let success = Substring(utf8).withCString { cString in - swiftahc_cshims_strptime_l(cString, format, &timeComponents, posixLocale) - } - return success ? timeComponents : nil -} - -private func parseCookieTime(_ timestampUTF8: String.UTF8View.SubSequence) -> Int64? { - // 0x20: Control characters or 0x7F: DEL - if timestampUTF8.contains(where: { $0 < 0x20 || $0 == 0x7F }) { - return nil - } - var timestampUTF8 = timestampUTF8 - if timestampUTF8.hasSuffix("GMT".utf8) { - let timezoneStart = timestampUTF8.index(timestampUTF8.endIndex, offsetBy: -3) - timestampUTF8 = timestampUTF8[.. Proxy { - .init(host: host, port: port, type: .http(nil)) - } - - /// Create a HTTP proxy. - /// - /// - parameters: - /// - host: proxy server host. - /// - port: proxy server port. - /// - authorization: proxy server authorization. - public static func server(host: String, port: Int, authorization: HTTPClient.Authorization? = nil) -> Proxy { - .init(host: host, port: port, type: .http(authorization)) - } - - /// Create a SOCKSv5 proxy. - /// - parameter host: The SOCKSv5 proxy address. - /// - parameter port: The SOCKSv5 proxy port, defaults to 1080. - /// - returns: A new instance of `Proxy` configured to connect to a `SOCKSv5` server. - public static func socksServer(host: String, port: Int = 1080) -> Proxy { - .init(host: host, port: port, type: .socks) - } - } -} diff --git a/Sources/AsyncHTTPClient/HTTPClient+StructuredConcurrency.swift b/Sources/AsyncHTTPClient/HTTPClient+StructuredConcurrency.swift deleted file mode 100644 index f7d471f10..000000000 --- a/Sources/AsyncHTTPClient/HTTPClient+StructuredConcurrency.swift +++ /dev/null @@ -1,72 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2025 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIO - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension HTTPClient { - #if compiler(>=6.0) - /// Start & automatically shut down a new ``HTTPClient``. - /// - /// This method allows to start & automatically dispose of a ``HTTPClient`` following the principle of Structured Concurrency. - /// The ``HTTPClient`` is guaranteed to be shut down upon return, whether `body` throws or not. - /// - /// This may be particularly useful if you cannot use the shared singleton (``HTTPClient/shared``). - public static func withHTTPClient( - eventLoopGroup: any EventLoopGroup = HTTPClient.defaultEventLoopGroup, - configuration: Configuration = Configuration(), - backgroundActivityLogger: Logger? = nil, - isolation: isolated (any Actor)? = #isolation, - _ body: (HTTPClient) async throws -> Return - ) async throws -> Return { - let logger = (backgroundActivityLogger ?? HTTPClient.loggingDisabled) - let httpClient = HTTPClient( - eventLoopGroup: eventLoopGroup, - configuration: configuration, - backgroundActivityLogger: logger - ) - return try await asyncDo { - try await body(httpClient) - } finally: { _ in - try await httpClient.shutdown() - } - } - #else - /// Start & automatically shut down a new ``HTTPClient``. - /// - /// This method allows to start & automatically dispose of a ``HTTPClient`` following the principle of Structured Concurrency. - /// The ``HTTPClient`` is guaranteed to be shut down upon return, whether `body` throws or not. - /// - /// This may be particularly useful if you cannot use the shared singleton (``HTTPClient/shared``). - public static func withHTTPClient( - eventLoopGroup: any EventLoopGroup = HTTPClient.defaultEventLoopGroup, - configuration: Configuration = Configuration(), - backgroundActivityLogger: Logger? = nil, - _ body: (HTTPClient) async throws -> Return - ) async throws -> Return { - let logger = (backgroundActivityLogger ?? HTTPClient.loggingDisabled) - let httpClient = HTTPClient( - eventLoopGroup: eventLoopGroup, - configuration: configuration, - backgroundActivityLogger: logger - ) - return try await asyncDo { - try await body(httpClient) - } finally: { _ in - try await httpClient.shutdown() - } - } - #endif -} diff --git a/Sources/AsyncHTTPClient/HTTPClient.swift b/Sources/AsyncHTTPClient/HTTPClient.swift deleted file mode 100644 index e628c6073..000000000 --- a/Sources/AsyncHTTPClient/HTTPClient.swift +++ /dev/null @@ -1,1467 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2018-2019 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Atomics -import Foundation -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOHTTP1 -import NIOHTTPCompression -import NIOPosix -import NIOSSL -import NIOTLS -import NIOTransportServices - -extension Logger { - private func requestInfo(_ request: HTTPClient.Request) -> Logger.Metadata.Value { - "\(request.method) \(request.url)" - } - - func attachingRequestInformation(_ request: HTTPClient.Request, requestID: Int) -> Logger { - var modified = self - modified[metadataKey: "ahc-prev-request"] = nil - modified[metadataKey: "ahc-request-id"] = "\(requestID)" - return modified - } -} - -let globalRequestID = ManagedAtomic(0) - -/// HTTPClient class provides API for request execution. -/// -/// Example: -/// -/// ```swift -/// HTTPClient.shared.get(url: "https://swift.org/", deadline: .now() + .seconds(1)).whenComplete { result in -/// switch result { -/// case .failure(let error): -/// // process error -/// case .success(let response): -/// if let response.status == .ok { -/// // handle response -/// } else { -/// // handle remote error -/// } -/// } -/// } -/// ``` -public class HTTPClient { - /// The `EventLoopGroup` in use by this ``HTTPClient``. - /// - /// All HTTP transactions will occur on loops owned by this group. - public let eventLoopGroup: EventLoopGroup - let configuration: Configuration - let poolManager: HTTPConnectionPool.Manager - - /// Shared thread pool used for file IO. It is lazily created on first access of ``Task/fileIOThreadPool``. - private var fileIOThreadPool: NIOThreadPool? - private let fileIOThreadPoolLock = NIOLock() - - private var state: State - private let stateLock = NIOLock() - private let canBeShutDown: Bool - - static let loggingDisabled = Logger(label: "AHC-do-not-log", factory: { _ in SwiftLogNoOpLogHandler() }) - - /// Create an ``HTTPClient`` with specified `EventLoopGroup` provider and configuration. - /// - /// - parameters: - /// - eventLoopGroupProvider: Specify how `EventLoopGroup` will be created. - /// - configuration: Client configuration. - public convenience init( - eventLoopGroupProvider: EventLoopGroupProvider, - configuration: Configuration = Configuration() - ) { - self.init( - eventLoopGroupProvider: eventLoopGroupProvider, - configuration: configuration, - backgroundActivityLogger: HTTPClient.loggingDisabled - ) - } - - /// Create an ``HTTPClient`` with specified `EventLoopGroup` and configuration. - /// - /// - parameters: - /// - eventLoopGroup: Specify how `EventLoopGroup` will be created. - /// - configuration: Client configuration. - public convenience init( - eventLoopGroup: EventLoopGroup = HTTPClient.defaultEventLoopGroup, - configuration: Configuration = Configuration() - ) { - self.init( - eventLoopGroupProvider: .shared(eventLoopGroup), - configuration: configuration, - backgroundActivityLogger: HTTPClient.loggingDisabled - ) - } - - /// Create an ``HTTPClient`` with specified `EventLoopGroup` provider and configuration. - /// - /// - parameters: - /// - eventLoopGroupProvider: Specify how `EventLoopGroup` will be created. - /// - configuration: Client configuration. - /// - backgroundActivityLogger: The logger to use for background activity logs. - public convenience init( - eventLoopGroupProvider: EventLoopGroupProvider, - configuration: Configuration = Configuration(), - backgroundActivityLogger: Logger - ) { - let eventLoopGroup: any EventLoopGroup - - switch eventLoopGroupProvider { - case .shared(let group): - eventLoopGroup = group - default: // handle `.createNew` without a deprecation warning - eventLoopGroup = HTTPClient.defaultEventLoopGroup - } - - self.init( - eventLoopGroup: eventLoopGroup, - configuration: configuration, - backgroundActivityLogger: backgroundActivityLogger - ) - } - - /// Create an ``HTTPClient`` with specified `EventLoopGroup` and configuration. - /// - /// - parameters: - /// - eventLoopGroup: The `EventLoopGroup` that the ``HTTPClient`` will use. - /// - configuration: Client configuration. - /// - backgroundActivityLogger: The `Logger` that will be used to log background any activity that's not associated with a request. - public convenience init( - eventLoopGroup: any EventLoopGroup = HTTPClient.defaultEventLoopGroup, - configuration: Configuration = Configuration(), - backgroundActivityLogger: Logger - ) { - self.init( - eventLoopGroup: eventLoopGroup, - configuration: configuration, - backgroundActivityLogger: backgroundActivityLogger, - canBeShutDown: true - ) - } - - internal required init( - eventLoopGroup: EventLoopGroup, - configuration: Configuration = Configuration(), - backgroundActivityLogger: Logger, - canBeShutDown: Bool - ) { - self.canBeShutDown = canBeShutDown - self.eventLoopGroup = eventLoopGroup - self.configuration = configuration - self.poolManager = HTTPConnectionPool.Manager( - eventLoopGroup: self.eventLoopGroup, - configuration: self.configuration, - backgroundActivityLogger: backgroundActivityLogger - ) - self.state = .upAndRunning - } - - deinit { - debugOnly { - // We want to crash only in debug mode. - switch self.state { - case .shutDown: - break - case .shuttingDown: - preconditionFailure( - """ - This state should be totally unreachable. While the HTTPClient is shutting down a \ - reference cycle should exist, that prevents it from deinit. - """ - ) - case .upAndRunning: - preconditionFailure( - """ - Client not shut down before the deinit. Please call client.shutdown() when no \ - longer needed. Otherwise memory will leak. - """ - ) - } - } - } - - /// Shuts down the client and `EventLoopGroup` if it was created by the client. - /// - /// This method blocks the thread indefinitely, prefer using ``shutdown()-96ayw``. - @available(*, noasync, message: "syncShutdown() can block indefinitely, prefer shutdown()", renamed: "shutdown()") - public func syncShutdown() throws { - try self.syncShutdown(requiresCleanClose: false) - } - - /// Shuts down the client and `EventLoopGroup` if it was created by the client. - /// - /// - parameters: - /// - requiresCleanClose: Determine if the client should throw when it is shutdown in a non-clean state - /// - /// - Note: - /// The `requiresCleanClose` will let the client do additional checks about its internal consistency on shutdown and - /// throw the appropriate error if needed. For instance, if its internal connection pool has any non-released connections, - /// this indicate shutdown was called too early before tasks were completed or explicitly canceled. - /// In general, setting this parameter to `true` should make it easier and faster to catch related programming errors. - func syncShutdown(requiresCleanClose: Bool) throws { - if let eventLoop = MultiThreadedEventLoopGroup.currentEventLoop { - preconditionFailure( - """ - BUG DETECTED: syncShutdown() must not be called when on an EventLoop. - Calling syncShutdown() on any EventLoop can lead to deadlocks. - Current eventLoop: \(eventLoop) - """ - ) - } - - final class ShutdownError: @unchecked Sendable { - // @unchecked because error is protected by lock. - - // Stores whether the shutdown has happened or not. - private let lock: ConditionLock - private var error: Error? - - init() { - self.error = nil - self.lock = ConditionLock(value: false) - } - - func didShutdown(_ error: (any Error)?) { - self.lock.lock(whenValue: false) - defer { - self.lock.unlock(withValue: true) - } - self.error = error - } - - func blockUntilShutdown() -> (any Error)? { - self.lock.lock(whenValue: true) - defer { - self.lock.unlock(withValue: true) - } - return self.error - } - } - - let shutdownError = ShutdownError() - - self.shutdown( - requiresCleanClose: requiresCleanClose, - queue: DispatchQueue(label: "async-http-client.shutdown") - ) { error in - shutdownError.didShutdown(error) - } - - let error = shutdownError.blockUntilShutdown() - - if let error = error { - throw error - } - } - - /// Shuts down the client and event loop gracefully. - /// - /// This function is clearly an outlier in that it uses a completion - /// callback instead of an EventLoopFuture. The reason for that is that NIO's EventLoopFutures will call back on an event loop. - /// The virtue of this function is to shut the event loop down. To work around that we call back on a DispatchQueue - /// instead. - @preconcurrency public func shutdown( - queue: DispatchQueue = .global(), - _ callback: @Sendable @escaping (Error?) -> Void - ) { - self.shutdown(requiresCleanClose: false, queue: queue, callback) - } - - /// Shuts down the ``HTTPClient`` and releases its resources. - public func shutdown() -> EventLoopFuture { - let promise = self.eventLoopGroup.any().makePromise(of: Void.self) - self.shutdown(queue: .global()) { error in - if let error = error { - promise.fail(error) - } else { - promise.succeed(()) - } - } - return promise.futureResult - } - - private func shutdown(requiresCleanClose: Bool, queue: DispatchQueue, _ callback: @escaping ShutdownCallback) { - guard self.canBeShutDown else { - queue.async { - callback(HTTPClientError.shutdownUnsupported) - } - return - } - do { - try self.stateLock.withLock { - guard case .upAndRunning = self.state else { - throw HTTPClientError.alreadyShutdown - } - self.state = .shuttingDown(requiresCleanClose: requiresCleanClose, callback: callback) - } - } catch { - callback(error) - return - } - - let promise = self.eventLoopGroup.any().makePromise(of: Bool.self) - self.poolManager.shutdown(promise: promise) - promise.futureResult.whenComplete { result in - switch result { - case .failure: - preconditionFailure("Shutting down the connection pool must not fail, ever.") - case .success(let unclean): - let (callback, uncleanError) = self.stateLock.withLock { () -> (ShutdownCallback, Error?) in - guard case .shuttingDown(let requiresClean, callback: let callback) = self.state else { - preconditionFailure("Why did the pool manager shut down, if it was not instructed to") - } - - let error: Error? = (requiresClean && unclean) ? HTTPClientError.uncleanShutdown : nil - return (callback, error) - } - self.stateLock.withLock { - self.state = .shutDown - } - queue.async { - callback(uncleanError) - } - } - } - } - - @Sendable - private func makeOrGetFileIOThreadPool() -> NIOThreadPool { - self.fileIOThreadPoolLock.withLock { - guard let fileIOThreadPool = self.fileIOThreadPool else { - return NIOThreadPool.singleton - } - return fileIOThreadPool - } - } - - /// Execute `GET` request using specified URL. - /// - /// - parameters: - /// - url: Remote URL. - /// - deadline: Point in time by which the request must complete. - public func get(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture { - self.get(url: url, deadline: deadline, logger: HTTPClient.loggingDisabled) - } - - /// Execute `GET` request using specified URL. - /// - /// - parameters: - /// - url: Remote URL. - /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. - public func get(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture { - self.execute(.GET, url: url, deadline: deadline, logger: logger) - } - - /// Execute `POST` request using specified URL. - /// - /// - parameters: - /// - url: Remote URL. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. - public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture { - self.post(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled) - } - - /// Execute `POST` request using specified URL. - /// - /// - parameters: - /// - url: Remote URL. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. - public func post( - url: String, - body: Body? = nil, - deadline: NIODeadline? = nil, - logger: Logger - ) -> EventLoopFuture { - self.execute(.POST, url: url, body: body, deadline: deadline, logger: logger) - } - - /// Execute `PATCH` request using specified URL. - /// - /// - parameters: - /// - url: Remote URL. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. - public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture { - self.patch(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled) - } - - /// Execute `PATCH` request using specified URL. - /// - /// - parameters: - /// - url: Remote URL. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. - public func patch( - url: String, - body: Body? = nil, - deadline: NIODeadline? = nil, - logger: Logger - ) -> EventLoopFuture { - self.execute(.PATCH, url: url, body: body, deadline: deadline, logger: logger) - } - - /// Execute `PUT` request using specified URL. - /// - /// - parameters: - /// - url: Remote URL. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. - public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture { - self.put(url: url, body: body, deadline: deadline, logger: HTTPClient.loggingDisabled) - } - - /// Execute `PUT` request using specified URL. - /// - /// - parameters: - /// - url: Remote URL. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. - public func put( - url: String, - body: Body? = nil, - deadline: NIODeadline? = nil, - logger: Logger - ) -> EventLoopFuture { - self.execute(.PUT, url: url, body: body, deadline: deadline, logger: logger) - } - - /// Execute `DELETE` request using specified URL. - /// - /// - parameters: - /// - url: Remote URL. - /// - deadline: The time when the request must have been completed by. - public func delete(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture { - self.delete(url: url, deadline: deadline, logger: HTTPClient.loggingDisabled) - } - - /// Execute `DELETE` request using specified URL. - /// - /// - parameters: - /// - url: Remote URL. - /// - deadline: The time when the request must have been completed by. - /// - logger: The logger to use for this request. - public func delete(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture { - self.execute(.DELETE, url: url, deadline: deadline, logger: logger) - } - - /// Execute arbitrary HTTP request using specified URL. - /// - /// - parameters: - /// - method: Request method. - /// - url: Request url. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. - public func execute( - _ method: HTTPMethod = .GET, - url: String, - body: Body? = nil, - deadline: NIODeadline? = nil, - logger: Logger? = nil - ) -> EventLoopFuture { - do { - let request = try Request(url: url, method: method, body: body) - return self.execute(request: request, deadline: deadline, logger: logger ?? HTTPClient.loggingDisabled) - } catch { - return self.eventLoopGroup.any().makeFailedFuture(error) - } - } - - /// Execute arbitrary HTTP+UNIX request to a unix domain socket path, using the specified URL as the request to send to the server. - /// - /// - parameters: - /// - method: Request method. - /// - socketPath: The path to the unix domain socket to connect to. - /// - urlPath: The URL path and query that will be sent to the server. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. - public func execute( - _ method: HTTPMethod = .GET, - socketPath: String, - urlPath: String, - body: Body? = nil, - deadline: NIODeadline? = nil, - logger: Logger? = nil - ) -> EventLoopFuture { - do { - guard let url = URL(httpURLWithSocketPath: socketPath, uri: urlPath) else { - throw HTTPClientError.invalidURL - } - let request = try Request(url: url, method: method, body: body) - return self.execute(request: request, deadline: deadline, logger: logger ?? HTTPClient.loggingDisabled) - } catch { - return self.eventLoopGroup.any().makeFailedFuture(error) - } - } - - /// Execute arbitrary HTTPS+UNIX request to a unix domain socket path over TLS, using the specified URL as the request to send to the server. - /// - /// - parameters: - /// - method: Request method. - /// - secureSocketPath: The path to the unix domain socket to connect to. - /// - urlPath: The URL path and query that will be sent to the server. - /// - body: Request body. - /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. - public func execute( - _ method: HTTPMethod = .GET, - secureSocketPath: String, - urlPath: String, - body: Body? = nil, - deadline: NIODeadline? = nil, - logger: Logger? = nil - ) -> EventLoopFuture { - do { - guard let url = URL(httpsURLWithSocketPath: secureSocketPath, uri: urlPath) else { - throw HTTPClientError.invalidURL - } - let request = try Request(url: url, method: method, body: body) - return self.execute(request: request, deadline: deadline, logger: logger ?? HTTPClient.loggingDisabled) - } catch { - return self.eventLoopGroup.any().makeFailedFuture(error) - } - } - - /// Execute arbitrary HTTP request using specified URL. - /// - /// - parameters: - /// - request: HTTP request to execute. - /// - deadline: Point in time by which the request must complete. - public func execute(request: Request, deadline: NIODeadline? = nil) -> EventLoopFuture { - self.execute(request: request, deadline: deadline, logger: HTTPClient.loggingDisabled) - } - - /// Execute arbitrary HTTP request using specified URL. - /// - /// - parameters: - /// - request: HTTP request to execute. - /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. - public func execute(request: Request, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture { - let accumulator = ResponseAccumulator(request: request) - return self.execute(request: request, delegate: accumulator, deadline: deadline, logger: logger).futureResult - } - - /// Execute arbitrary HTTP request using specified URL. - /// - /// - parameters: - /// - request: HTTP request to execute. - /// - eventLoop: NIO Event Loop preference. - /// - deadline: Point in time by which the request must complete. - public func execute( - request: Request, - eventLoop: EventLoopPreference, - deadline: NIODeadline? = nil - ) -> EventLoopFuture { - self.execute( - request: request, - eventLoop: eventLoop, - deadline: deadline, - logger: HTTPClient.loggingDisabled - ) - } - - /// Execute arbitrary HTTP request and handle response processing using provided delegate. - /// - /// - parameters: - /// - request: HTTP request to execute. - /// - eventLoopPreference: NIO Event Loop preference. - /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. - public func execute( - request: Request, - eventLoop eventLoopPreference: EventLoopPreference, - deadline: NIODeadline? = nil, - logger: Logger? - ) -> EventLoopFuture { - let accumulator = ResponseAccumulator(request: request) - return self.execute( - request: request, - delegate: accumulator, - eventLoop: eventLoopPreference, - deadline: deadline, - logger: logger - ).futureResult - } - - /// Execute arbitrary HTTP request and handle response processing using provided delegate. - /// - /// - parameters: - /// - request: HTTP request to execute. - /// - delegate: Delegate to process response parts. - /// - deadline: Point in time by which the request must complete. - public func execute( - request: Request, - delegate: Delegate, - deadline: NIODeadline? = nil - ) -> Task { - self.execute(request: request, delegate: delegate, deadline: deadline, logger: HTTPClient.loggingDisabled) - } - - /// Execute arbitrary HTTP request and handle response processing using provided delegate. - /// - /// - parameters: - /// - request: HTTP request to execute. - /// - delegate: Delegate to process response parts. - /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. - public func execute( - request: Request, - delegate: Delegate, - deadline: NIODeadline? = nil, - logger: Logger - ) -> Task { - self.execute(request: request, delegate: delegate, eventLoop: .indifferent, deadline: deadline, logger: logger) - } - - /// Execute arbitrary HTTP request and handle response processing using provided delegate. - /// - /// - parameters: - /// - request: HTTP request to execute. - /// - delegate: Delegate to process response parts. - /// - eventLoopPreference: NIO Event Loop preference. - /// - deadline: Point in time by which the request must complete. - public func execute( - request: Request, - delegate: Delegate, - eventLoop eventLoopPreference: EventLoopPreference, - deadline: NIODeadline? = nil - ) -> Task { - self.execute( - request: request, - delegate: delegate, - eventLoop: eventLoopPreference, - deadline: deadline, - logger: HTTPClient.loggingDisabled - ) - } - - /// Execute arbitrary HTTP request and handle response processing using provided delegate. - /// - /// - parameters: - /// - request: HTTP request to execute. - /// - delegate: Delegate to process response parts. - /// - eventLoopPreference: NIO Event Loop preference. - /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. - public func execute( - request: Request, - delegate: Delegate, - eventLoop eventLoopPreference: EventLoopPreference, - deadline: NIODeadline? = nil, - logger: Logger? - ) -> Task { - self._execute( - request: request, - delegate: delegate, - eventLoop: eventLoopPreference, - deadline: deadline, - logger: logger, - redirectState: RedirectState( - self.configuration.redirectConfiguration.mode, - initialURL: request.url.absoluteString - ) - ) - } - - /// Execute arbitrary HTTP request and handle response processing using provided delegate. - /// - /// - parameters: - /// - request: HTTP request to execute. - /// - delegate: Delegate to process response parts. - /// - eventLoop: NIO Event Loop preference. - /// - deadline: Point in time by which the request must complete. - /// - logger: The logger to use for this request. - func _execute( - request: Request, - delegate: Delegate, - eventLoop eventLoopPreference: EventLoopPreference, - deadline: NIODeadline? = nil, - logger originalLogger: Logger?, - redirectState: RedirectState? - ) -> Task { - let logger = (originalLogger ?? HTTPClient.loggingDisabled).attachingRequestInformation( - request, - requestID: globalRequestID.wrappingIncrementThenLoad(ordering: .relaxed) - ) - let taskEL: EventLoop - switch eventLoopPreference.preference { - case .indifferent: - // if possible we want a connection on the current `EventLoop` - taskEL = self.eventLoopGroup.any() - case .delegate(on: let eventLoop): - precondition( - self.eventLoopGroup.makeIterator().contains { $0 === eventLoop }, - "Provided EventLoop must be part of clients EventLoopGroup." - ) - taskEL = eventLoop - case .delegateAndChannel(on: let eventLoop): - precondition( - self.eventLoopGroup.makeIterator().contains { $0 === eventLoop }, - "Provided EventLoop must be part of clients EventLoopGroup." - ) - taskEL = eventLoop - case .testOnly_exact(_, delegateOn: let delegateEL): - taskEL = delegateEL - } - - logger.trace( - "selected EventLoop for task given the preference", - metadata: [ - "ahc-eventloop": "\(taskEL)", - "ahc-el-preference": "\(eventLoopPreference)", - ] - ) - - let failedTask: Task? = self.stateLock.withLock { - switch self.state { - case .upAndRunning: - return nil - case .shuttingDown, .shutDown: - logger.debug("client is shutting down, failing request") - return Task.failedTask( - eventLoop: taskEL, - error: HTTPClientError.alreadyShutdown, - logger: logger, - makeOrGetFileIOThreadPool: self.makeOrGetFileIOThreadPool - ) - } - } - - if let failedTask = failedTask { - return failedTask - } - - let redirectHandler: RedirectHandler? = { - guard let redirectState = redirectState else { return nil } - - return .init(request: request, redirectState: redirectState) { newRequest, newRedirectState in - self._execute( - request: newRequest, - delegate: delegate, - eventLoop: eventLoopPreference, - deadline: deadline, - logger: logger, - redirectState: newRedirectState - ) - } - }() - - let task = Task( - eventLoop: taskEL, - logger: logger, - makeOrGetFileIOThreadPool: self.makeOrGetFileIOThreadPool - ) - do { - let requestBag = try RequestBag( - request: request, - eventLoopPreference: eventLoopPreference, - task: task, - redirectHandler: redirectHandler, - connectionDeadline: .now() + (self.configuration.timeout.connectionCreationTimeout), - requestOptions: .fromClientConfiguration(self.configuration), - delegate: delegate - ) - - if let deadline = deadline { - let deadlineSchedule = taskEL.scheduleTask(deadline: deadline) { - requestBag.deadlineExceeded() - } - - task.promise.futureResult.whenComplete { _ in - deadlineSchedule.cancel() - } - } - - self.poolManager.executeRequest(requestBag) - } catch { - delegate.didReceiveError(task: task, error) - task.failInternal(with: error) - } - - return task - } - - /// ``HTTPClient`` configuration. - public struct Configuration { - /// TLS configuration, defaults to `TLSConfiguration.makeClientConfiguration()`. - public var tlsConfiguration: Optional - - /// Sometimes it can be useful to connect to one host e.g. `x.example.com` but - /// request and validate the certificate chain as if we would connect to `y.example.com`. - /// ``dnsOverride`` allows to do just that by mapping host names which we will request and validate the certificate chain, to a different - /// host name which will be used to actually connect to. - /// - /// **Example:** if ``dnsOverride`` is set to `["example.com": "localhost"]` and we execute a request with a - /// `url` of `https://example.com/`, the ``HTTPClient`` will actually open a connection to `localhost` instead of `example.com`. - /// ``HTTPClient`` will still request certificates from the server for `example.com` and validate them as if we would connect to `example.com`. - public var dnsOverride: [String: String] = [:] - - /// Enables following 3xx redirects automatically. - /// - /// Following redirects are supported: - /// - `301: Moved Permanently` - /// - `302: Found` - /// - `303: See Other` - /// - `304: Not Modified` - /// - `305: Use Proxy` - /// - `307: Temporary Redirect` - /// - `308: Permanent Redirect` - public var redirectConfiguration: RedirectConfiguration - /// Default client timeout, defaults to no ``Timeout-swift.struct/read`` timeout - /// and 10 seconds ``Timeout-swift.struct/connect`` timeout. - public var timeout: Timeout - /// Connection pool configuration. - public var connectionPool: ConnectionPool - /// Upstream proxy, defaults to no proxy. - public var proxy: Proxy? - /// Enables automatic body decompression. Supported algorithms are gzip and deflate. - public var decompression: Decompression - /// Ignore TLS unclean shutdown error, defaults to `false`. - @available( - *, - deprecated, - message: - "AsyncHTTPClient now correctly supports handling unexpected SSL connection drops. This property is ignored" - ) - public var ignoreUncleanSSLShutdown: Bool { - get { false } - set {} - } - - /// What HTTP versions to use. - /// - /// Set to ``HTTPVersion-swift.struct/automatic`` by default which will use HTTP/2 if run over https and the server supports it, otherwise HTTP/1 - public var httpVersion: HTTPVersion - - /// Whether ``HTTPClient`` will let Network.framework sit in the `.waiting` state awaiting new network changes, or fail immediately. Defaults to `true`, - /// which is the recommended setting. Only set this to `false` when attempting to trigger a particular error path. - public var networkFrameworkWaitForConnectivity: Bool - - /// The maximum number of times each connection can be used before it is replaced with a new one. Use `nil` (the default) - /// if no limit should be applied to each connection. - /// - /// - Precondition: The value must be greater than zero. - public var maximumUsesPerConnection: Int? { - willSet { - if let newValue = newValue, newValue <= 0 { - fatalError("maximumUsesPerConnection must be greater than zero or nil") - } - } - } - - /// Whether ``HTTPClient`` will use Multipath TCP or not - /// By default, don't use it - public var enableMultipath: Bool - - /// A method with access to the HTTP/1 connection channel that is called when creating the connection. - public var http1_1ConnectionDebugInitializer: (@Sendable (Channel) -> EventLoopFuture)? - - /// A method with access to the HTTP/2 connection channel that is called when creating the connection. - public var http2ConnectionDebugInitializer: (@Sendable (Channel) -> EventLoopFuture)? - - /// A method with access to the HTTP/2 stream channel that is called when creating the stream. - public var http2StreamChannelDebugInitializer: (@Sendable (Channel) -> EventLoopFuture)? - - public init( - tlsConfiguration: TLSConfiguration? = nil, - redirectConfiguration: RedirectConfiguration? = nil, - timeout: Timeout = Timeout(), - connectionPool: ConnectionPool = ConnectionPool(), - proxy: Proxy? = nil, - ignoreUncleanSSLShutdown: Bool = false, - decompression: Decompression = .disabled - ) { - self.tlsConfiguration = tlsConfiguration - self.redirectConfiguration = redirectConfiguration ?? RedirectConfiguration() - self.timeout = timeout - self.connectionPool = connectionPool - self.proxy = proxy - self.decompression = decompression - self.httpVersion = .automatic - self.networkFrameworkWaitForConnectivity = true - self.enableMultipath = false - } - - public init( - tlsConfiguration: TLSConfiguration? = nil, - redirectConfiguration: RedirectConfiguration? = nil, - timeout: Timeout = Timeout(), - proxy: Proxy? = nil, - ignoreUncleanSSLShutdown: Bool = false, - decompression: Decompression = .disabled - ) { - self.init( - tlsConfiguration: tlsConfiguration, - redirectConfiguration: redirectConfiguration, - timeout: timeout, - connectionPool: ConnectionPool(), - proxy: proxy, - ignoreUncleanSSLShutdown: ignoreUncleanSSLShutdown, - decompression: decompression - ) - } - - public init( - certificateVerification: CertificateVerification, - redirectConfiguration: RedirectConfiguration? = nil, - timeout: Timeout = Timeout(), - maximumAllowedIdleTimeInConnectionPool: TimeAmount = .seconds(60), - proxy: Proxy? = nil, - ignoreUncleanSSLShutdown: Bool = false, - decompression: Decompression = .disabled - ) { - var tlsConfig = TLSConfiguration.makeClientConfiguration() - tlsConfig.certificateVerification = certificateVerification - self.init( - tlsConfiguration: tlsConfig, - redirectConfiguration: redirectConfiguration, - timeout: timeout, - connectionPool: ConnectionPool(idleTimeout: maximumAllowedIdleTimeInConnectionPool), - proxy: proxy, - ignoreUncleanSSLShutdown: ignoreUncleanSSLShutdown, - decompression: decompression - ) - } - - public init( - certificateVerification: CertificateVerification, - redirectConfiguration: RedirectConfiguration? = nil, - timeout: Timeout = Timeout(), - connectionPool: TimeAmount = .seconds(60), - proxy: Proxy? = nil, - ignoreUncleanSSLShutdown: Bool = false, - decompression: Decompression = .disabled, - backgroundActivityLogger: Logger? - ) { - var tlsConfig = TLSConfiguration.makeClientConfiguration() - tlsConfig.certificateVerification = certificateVerification - self.init( - tlsConfiguration: tlsConfig, - redirectConfiguration: redirectConfiguration, - timeout: timeout, - connectionPool: ConnectionPool(idleTimeout: connectionPool), - proxy: proxy, - ignoreUncleanSSLShutdown: ignoreUncleanSSLShutdown, - decompression: decompression - ) - } - - public init( - certificateVerification: CertificateVerification, - redirectConfiguration: RedirectConfiguration? = nil, - timeout: Timeout = Timeout(), - proxy: Proxy? = nil, - ignoreUncleanSSLShutdown: Bool = false, - decompression: Decompression = .disabled - ) { - self.init( - certificateVerification: certificateVerification, - redirectConfiguration: redirectConfiguration, - timeout: timeout, - maximumAllowedIdleTimeInConnectionPool: .seconds(60), - proxy: proxy, - ignoreUncleanSSLShutdown: ignoreUncleanSSLShutdown, - decompression: decompression - ) - } - - public init( - tlsConfiguration: TLSConfiguration? = nil, - redirectConfiguration: RedirectConfiguration? = nil, - timeout: Timeout = Timeout(), - connectionPool: ConnectionPool = ConnectionPool(), - proxy: Proxy? = nil, - ignoreUncleanSSLShutdown: Bool = false, - decompression: Decompression = .disabled, - http1_1ConnectionDebugInitializer: (@Sendable (Channel) -> EventLoopFuture)? = nil, - http2ConnectionDebugInitializer: (@Sendable (Channel) -> EventLoopFuture)? = nil, - http2StreamChannelDebugInitializer: (@Sendable (Channel) -> EventLoopFuture)? = nil - ) { - self.init( - tlsConfiguration: tlsConfiguration, - redirectConfiguration: redirectConfiguration, - timeout: timeout, - connectionPool: connectionPool, - proxy: proxy, - ignoreUncleanSSLShutdown: ignoreUncleanSSLShutdown, - decompression: decompression - ) - self.http1_1ConnectionDebugInitializer = http1_1ConnectionDebugInitializer - self.http2ConnectionDebugInitializer = http2ConnectionDebugInitializer - self.http2StreamChannelDebugInitializer = http2StreamChannelDebugInitializer - } - } - - /// Specifies how `EventLoopGroup` will be created and establishes lifecycle ownership. - public enum EventLoopGroupProvider { - /// `EventLoopGroup` will be provided by the user. Owner of this group is responsible for its lifecycle. - case shared(EventLoopGroup) - /// The original intention of this was that ``HTTPClient`` would create and own its own `EventLoopGroup` to - /// facilitate use in programs that are not already using SwiftNIO. - /// Since https://github.com/apple/swift-nio/pull/2471 however, SwiftNIO does provide a global, shared singleton - /// `EventLoopGroup`s that we can use. ``HTTPClient`` is no longer able to create & own its own - /// `EventLoopGroup` which solves a whole host of issues around shutdown. - @available(*, deprecated, renamed: "singleton", message: "Please use the singleton EventLoopGroup explicitly") - case createNew - } - - /// Specifies how the library will treat the event loop passed by the user. - public struct EventLoopPreference { - enum Preference { - /// Event Loop will be selected by the library. - case indifferent - /// The delegate will be run on the specified EventLoop (and the Channel if possible). - case delegate(on: EventLoop) - /// The delegate and the `Channel` will be run on the specified EventLoop. - case delegateAndChannel(on: EventLoop) - - case testOnly_exact(channelOn: EventLoop, delegateOn: EventLoop) - } - - var preference: Preference - - init(_ preference: Preference) { - self.preference = preference - } - - /// Event Loop will be selected by the library. - public static let indifferent = EventLoopPreference(.indifferent) - - /// The delegate will be run on the specified EventLoop (and the Channel if possible). - /// - /// This will call the configured delegate on `eventLoop` and will try to use a `Channel` on the same - /// `EventLoop` but will not establish a new network connection just to satisfy the `EventLoop` preference if - /// another existing connection on a different `EventLoop` is readily available from a connection pool. - public static func delegate(on eventLoop: EventLoop) -> EventLoopPreference { - EventLoopPreference(.delegate(on: eventLoop)) - } - - /// The delegate and the `Channel` will be run on the specified EventLoop. - /// - /// Use this for use-cases where you prefer a new connection to be established over re-using an existing - /// connection that might be on a different `EventLoop`. - public static func delegateAndChannel(on eventLoop: EventLoop) -> EventLoopPreference { - EventLoopPreference(.delegateAndChannel(on: eventLoop)) - } - } - - /// Specifies decompression settings. - public enum Decompression: Sendable { - /// Decompression is disabled. - case disabled - /// Decompression is enabled. - case enabled(limit: NIOHTTPDecompression.DecompressionLimit) - } - - typealias ShutdownCallback = @Sendable (Error?) -> Void - - enum State { - case upAndRunning - case shuttingDown(requiresCleanClose: Bool, callback: ShutdownCallback) - case shutDown - } -} - -extension HTTPClient.EventLoopGroupProvider { - /// Shares ``HTTPClient/defaultEventLoopGroup`` which is a singleton `EventLoopGroup` suitable for the platform. - public static var singleton: Self { - .shared(HTTPClient.defaultEventLoopGroup) - } -} - -extension HTTPClient { - /// Returns the default `EventLoopGroup` singleton, automatically selecting the best for the platform. - /// - /// This will select the concrete `EventLoopGroup` depending which platform this is running on. - public static var defaultEventLoopGroup: EventLoopGroup { - #if canImport(Network) - if #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) { - return NIOTSEventLoopGroup.singleton - } else { - return MultiThreadedEventLoopGroup.singleton - } - #else - return MultiThreadedEventLoopGroup.singleton - #endif - } -} - -extension HTTPClient.Configuration: Sendable {} - -extension HTTPClient.EventLoopGroupProvider: Sendable {} -extension HTTPClient.EventLoopPreference: Sendable {} - -// HTTPClient is thread-safe because its shared mutable state is protected through a lock -extension HTTPClient: @unchecked Sendable {} - -extension HTTPClient.Configuration { - /// Timeout configuration. - public struct Timeout: Sendable { - /// Specifies connect timeout. If no connect timeout is given, a default 10 seconds timeout will be applied. - public var connect: TimeAmount? - /// Specifies read timeout. - public var read: TimeAmount? - /// Specifies the maximum amount of time without bytes being written by the client before closing the connection. - public var write: TimeAmount? - - /// Internal connection creation timeout. Defaults the connect timeout to always contain a value. - var connectionCreationTimeout: TimeAmount { - self.connect ?? .seconds(10) - } - - /// Create timeout. - /// - /// - parameters: - /// - connect: `connect` timeout. Will default to 10 seconds, if no value is provided. - /// - read: `read` timeout. - public init( - connect: TimeAmount? = nil, - read: TimeAmount? = nil - ) { - self.connect = connect - self.read = read - } - - /// Create timeout. - /// - /// - parameters: - /// - connect: `connect` timeout. Will default to 10 seconds, if no value is provided. - /// - read: `read` timeout. - /// - write: `write` timeout. - public init( - connect: TimeAmount? = nil, - read: TimeAmount? = nil, - write: TimeAmount - ) { - self.connect = connect - self.read = read - self.write = write - } - } - - /// Specifies redirect processing settings. - public struct RedirectConfiguration: Sendable { - enum Mode { - /// Redirects are not followed. - case disallow - /// Redirects are followed with a specified limit. - case follow(max: Int, allowCycles: Bool) - } - - var mode: Mode - - init() { - self.mode = .follow(max: 5, allowCycles: false) - } - - init(configuration: Mode) { - self.mode = configuration - } - - /// Redirects are not followed. - public static let disallow = RedirectConfiguration(configuration: .disallow) - - /// Redirects are followed with a specified limit. - /// - /// - parameters: - /// - max: The maximum number of allowed redirects. - /// - allowCycles: Whether cycles are allowed. - /// - /// - warning: Cycle detection will keep all visited URLs in memory which means a malicious server could use this as a denial-of-service vector. - public static func follow(max: Int, allowCycles: Bool) -> RedirectConfiguration { - .init(configuration: .follow(max: max, allowCycles: allowCycles)) - } - } - - /// Connection pool configuration. - public struct ConnectionPool: Hashable, Sendable { - /// Specifies amount of time connections are kept idle in the pool. After this time has passed without a new - /// request the connections are closed. - public var idleTimeout: TimeAmount = .seconds(60) - - /// The maximum number of connections that are kept alive in the connection pool per host. If requests with - /// an explicit eventLoopRequirement are sent, this number might be exceeded due to overflow connections. - public var concurrentHTTP1ConnectionsPerHostSoftLimit: Int = 8 - - /// If true, ``HTTPClient`` will try to create new connections on connection failure with an exponential backoff. - /// Requests will only fail after the ``HTTPClient/Configuration/Timeout-swift.struct/connect`` timeout exceeded. - /// If false, all requests that have no assigned connection will fail immediately after a connection could not be established. - /// Defaults to `true`. - /// - warning: We highly recommend leaving this on. - /// It is very common that connections establishment is flaky at scale. - /// ``HTTPClient`` will automatically mitigate these kind of issues if this flag is turned on. - public var retryConnectionEstablishment: Bool = true - - public init() {} - - public init(idleTimeout: TimeAmount) { - self.idleTimeout = idleTimeout - } - - public init(idleTimeout: TimeAmount, concurrentHTTP1ConnectionsPerHostSoftLimit: Int) { - self.idleTimeout = idleTimeout - self.concurrentHTTP1ConnectionsPerHostSoftLimit = concurrentHTTP1ConnectionsPerHostSoftLimit - } - } - - public struct HTTPVersion: Sendable, Hashable { - enum Configuration { - case http1Only - case automatic - } - - /// We will only use HTTP/1, even if the server would supports HTTP/2 - public static let http1Only: Self = .init(configuration: .http1Only) - - /// HTTP/2 is used if we connect to a server with HTTPS and the server supports HTTP/2, otherwise we use HTTP/1 - public static let automatic: Self = .init(configuration: .automatic) - - var configuration: Configuration - } -} - -/// Possible client errors. -public struct HTTPClientError: Error, Equatable, CustomStringConvertible { - private enum Code: Equatable { - case invalidURL - case emptyHost - case missingSocketPath - case alreadyShutdown - case emptyScheme - case unsupportedScheme(String) - case readTimeout - case writeTimeout - case remoteConnectionClosed - case cancelled - case identityCodingIncorrectlyPresent - @available(*, deprecated, message: "AsyncHTTPClient now silently corrects this invalid header.") - case chunkedSpecifiedMultipleTimes - case invalidProxyResponse - case contentLengthMissing - case proxyAuthenticationRequired - case redirectLimitReached - case redirectCycleDetected - case uncleanShutdown - case traceRequestWithBody - case invalidHeaderFieldNames([String]) - case invalidHeaderFieldValues([String]) - case bodyLengthMismatch - case writeAfterRequestSent - @available(*, deprecated, message: "AsyncHTTPClient now silently corrects invalid headers.") - case incompatibleHeaders - case connectTimeout - case socksHandshakeTimeout - case httpProxyHandshakeTimeout - case tlsHandshakeTimeout - case serverOfferedUnsupportedApplicationProtocol(String) - case requestStreamCancelled - case getConnectionFromPoolTimeout - case deadlineExceeded - case httpEndReceivedAfterHeadWith1xx - case shutdownUnsupported - } - - private var code: Code - - private init(code: Code) { - self.code = code - } - - public var description: String { - "HTTPClientError.\(String(describing: self.code))" - } - - /// Short description of the error that can be used in case a bounded set of error descriptions is expected, e.g. to - /// include in metric labels. For this reason the description must not contain associated values. - public var shortDescription: String { - // When adding new cases here, do *not* include dynamic (associated) values in the description. - switch self.code { - case .invalidURL: - return "Invalid URL" - case .emptyHost: - return "Empty host" - case .missingSocketPath: - return "Missing socket path" - case .alreadyShutdown: - return "Already shutdown" - case .emptyScheme: - return "Empty scheme" - case .unsupportedScheme: - return "Unsupported scheme" - case .readTimeout: - return "Read timeout" - case .writeTimeout: - return "Write timeout" - case .remoteConnectionClosed: - return "Remote connection closed" - case .cancelled: - return "Cancelled" - case .identityCodingIncorrectlyPresent: - return "Identity coding incorrectly present" - case .chunkedSpecifiedMultipleTimes: - return "Chunked specified multiple times" - case .invalidProxyResponse: - return "Invalid proxy response" - case .contentLengthMissing: - return "Content length missing" - case .proxyAuthenticationRequired: - return "Proxy authentication required" - case .redirectLimitReached: - return "Redirect limit reached" - case .redirectCycleDetected: - return "Redirect cycle detected" - case .uncleanShutdown: - return "Unclean shutdown" - case .traceRequestWithBody: - return "Trace request with body" - case .invalidHeaderFieldNames: - return "Invalid header field names" - case .invalidHeaderFieldValues: - return "Invalid header field values" - case .bodyLengthMismatch: - return "Body length mismatch" - case .writeAfterRequestSent: - return "Write after request sent" - case .incompatibleHeaders: - return "Incompatible headers" - case .connectTimeout: - return "Connect timeout" - case .socksHandshakeTimeout: - return "SOCKS handshake timeout" - case .httpProxyHandshakeTimeout: - return "HTTP proxy handshake timeout" - case .tlsHandshakeTimeout: - return "TLS handshake timeout" - case .serverOfferedUnsupportedApplicationProtocol: - return "Server offered unsupported application protocol" - case .requestStreamCancelled: - return "Request stream cancelled" - case .getConnectionFromPoolTimeout: - return "Get connection from pool timeout" - case .deadlineExceeded: - return "Deadline exceeded" - case .httpEndReceivedAfterHeadWith1xx: - return "HTTP end received after head with 1xx" - case .shutdownUnsupported: - return "The global singleton HTTP client cannot be shut down" - } - } - - /// URL provided is invalid. - public static let invalidURL = HTTPClientError(code: .invalidURL) - /// URL does not contain host. - public static let emptyHost = HTTPClientError(code: .emptyHost) - /// URL does not contain a socketPath as a host for http(s)+unix shemes. - public static let missingSocketPath = HTTPClientError(code: .missingSocketPath) - /// Client is shutdown and cannot be used for new requests. - public static let alreadyShutdown = HTTPClientError(code: .alreadyShutdown) - /// URL does not contain scheme. - public static let emptyScheme = HTTPClientError(code: .emptyScheme) - /// Provided URL scheme is not supported, supported schemes are: `http` and `https` - public static func unsupportedScheme(_ scheme: String) -> HTTPClientError { - HTTPClientError(code: .unsupportedScheme(scheme)) - } - /// Request timed out while waiting for response. - public static let readTimeout = HTTPClientError(code: .readTimeout) - /// Request timed out. - public static let writeTimeout = HTTPClientError(code: .writeTimeout) - /// Remote connection was closed unexpectedly. - public static let remoteConnectionClosed = HTTPClientError(code: .remoteConnectionClosed) - /// Request was cancelled. - public static let cancelled = HTTPClientError(code: .cancelled) - /// Request contains invalid identity encoding. - public static let identityCodingIncorrectlyPresent = HTTPClientError(code: .identityCodingIncorrectlyPresent) - /// Request contains multiple chunks definitions. - @available(*, deprecated, message: "AsyncHTTPClient now silently corrects this invalid header.") - public static let chunkedSpecifiedMultipleTimes = HTTPClientError(code: .chunkedSpecifiedMultipleTimes) - /// Proxy response was invalid. - public static let invalidProxyResponse = HTTPClientError(code: .invalidProxyResponse) - /// Request does not contain `Content-Length` header. - public static let contentLengthMissing = HTTPClientError(code: .contentLengthMissing) - /// Proxy Authentication Required. - public static let proxyAuthenticationRequired = HTTPClientError(code: .proxyAuthenticationRequired) - /// Redirect Limit reached. - public static let redirectLimitReached = HTTPClientError(code: .redirectLimitReached) - /// Redirect Cycle detected. - public static let redirectCycleDetected = HTTPClientError(code: .redirectCycleDetected) - /// Unclean shutdown. - public static let uncleanShutdown = HTTPClientError(code: .uncleanShutdown) - /// A body was sent in a request with method TRACE. - public static let traceRequestWithBody = HTTPClientError(code: .traceRequestWithBody) - /// Header field names contain invalid characters. - public static func invalidHeaderFieldNames(_ names: [String]) -> HTTPClientError { - HTTPClientError(code: .invalidHeaderFieldNames(names)) - } - /// Header field values contain invalid characters. - public static func invalidHeaderFieldValues(_ values: [String]) -> HTTPClientError { - HTTPClientError(code: .invalidHeaderFieldValues(values)) - } - /// Body length is not equal to `Content-Length`. - public static let bodyLengthMismatch = HTTPClientError(code: .bodyLengthMismatch) - /// Body part was written after request was fully sent. - public static let writeAfterRequestSent = HTTPClientError(code: .writeAfterRequestSent) - /// Incompatible headers specified, for example `Transfer-Encoding` and `Content-Length`. - @available(*, deprecated, message: "AsyncHTTPClient now silently corrects invalid headers.") - public static let incompatibleHeaders = HTTPClientError(code: .incompatibleHeaders) - /// Creating a new tcp connection timed out - public static let connectTimeout = HTTPClientError(code: .connectTimeout) - /// The socks handshake timed out. - public static let socksHandshakeTimeout = HTTPClientError(code: .socksHandshakeTimeout) - /// The http proxy connection creation timed out. - public static let httpProxyHandshakeTimeout = HTTPClientError(code: .httpProxyHandshakeTimeout) - /// The tls handshake timed out. - public static let tlsHandshakeTimeout = HTTPClientError(code: .tlsHandshakeTimeout) - /// The remote server only offered an unsupported application protocol - public static func serverOfferedUnsupportedApplicationProtocol(_ proto: String) -> HTTPClientError { - HTTPClientError(code: .serverOfferedUnsupportedApplicationProtocol(proto)) - } - - /// The globally shared singleton ``HTTPClient`` cannot be shut down. - public static var shutdownUnsupported: HTTPClientError { - HTTPClientError(code: .shutdownUnsupported) - } - - /// The request deadline was exceeded. The request was cancelled because of this. - public static let deadlineExceeded = HTTPClientError(code: .deadlineExceeded) - - /// The remote server responded with a status code >= 300, before the full request was sent. The request stream - /// was therefore cancelled - public static let requestStreamCancelled = HTTPClientError(code: .requestStreamCancelled) - - /// Aquiring a HTTP connection from the connection pool timed out. - /// - /// This can have multiple reasons: - /// - A connection could not be created within the timout period. - /// - Tasks are not processed fast enough on the existing connections, to process all waiters in time - public static let getConnectionFromPoolTimeout = HTTPClientError(code: .getConnectionFromPoolTimeout) - - @available( - *, - deprecated, - message: - "AsyncHTTPClient now correctly supports informational headers. For this reason `httpEndReceivedAfterHeadWith1xx` will not be thrown anymore." - ) - public static let httpEndReceivedAfterHeadWith1xx = HTTPClientError(code: .httpEndReceivedAfterHeadWith1xx) -} diff --git a/Sources/AsyncHTTPClient/HTTPHandler.swift b/Sources/AsyncHTTPClient/HTTPHandler.swift deleted file mode 100644 index 8d92d8ef7..000000000 --- a/Sources/AsyncHTTPClient/HTTPHandler.swift +++ /dev/null @@ -1,1106 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2018-2019 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Algorithms -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOHTTP1 -import NIOPosix -import NIOSSL - -#if compiler(>=6.0) -import Foundation -#else -@preconcurrency import Foundation -#endif - -extension HTTPClient { - /// A request body. - public struct Body: Sendable { - /// A streaming uploader. - /// - /// ``StreamWriter`` abstracts - public struct StreamWriter: Sendable { - let closure: @Sendable (IOData) -> EventLoopFuture - - /// Create new ``HTTPClient/Body/StreamWriter`` - /// - /// - parameters: - /// - closure: function that will be called to write actual bytes to the channel. - @preconcurrency - public init(closure: @escaping @Sendable (IOData) -> EventLoopFuture) { - self.closure = closure - } - - /// Write data to server. - /// - /// - parameters: - /// - data: `IOData` to write. - public func write(_ data: IOData) -> EventLoopFuture { - self.closure(data) - } - - @inlinable - func writeChunks( - of bytes: Bytes, - maxChunkSize: Int - ) -> EventLoopFuture where Bytes.Element == UInt8, Bytes: Sendable { - // `StreamWriter` has design issues, for example - // - https://github.com/swift-server/async-http-client/issues/194 - // - https://github.com/swift-server/async-http-client/issues/264 - // - We're not told the EventLoop the task runs on and the user is free to return whatever EL they - // want. - // One important consideration then is that we must lock around the iterator because we could be hopping - // between threads. - typealias Iterator = EnumeratedSequence>.Iterator - typealias Chunk = (offset: Int, element: ChunksOfCountCollection.Element) - - // HACK (again, we're not told the right EventLoop): Let's write 0 bytes to make the user tell us... - return self.write(.byteBuffer(ByteBuffer())).flatMapWithEventLoop { (_, loop) in - func makeIteratorAndFirstChunk( - bytes: Bytes - ) -> (iterator: Iterator, chunk: Chunk)? { - var iterator = bytes.chunks(ofCount: maxChunkSize).enumerated().makeIterator() - guard let chunk = iterator.next() else { - return nil - } - - return (iterator, chunk) - } - - guard let iteratorAndChunk = makeIteratorAndFirstChunk(bytes: bytes) else { - return loop.makeSucceededVoidFuture() - } - - var iterator = iteratorAndChunk.0 - let chunk = iteratorAndChunk.1 - - // can't use closure here as we recursively call ourselves which closures can't do - func writeNextChunk(_ chunk: Chunk, allDone: EventLoopPromise) { - let loop = allDone.futureResult.eventLoop - loop.assertInEventLoop() - - if let (index, element) = iterator.next() { - self.write(.byteBuffer(ByteBuffer(bytes: chunk.element))).hop(to: loop).assumeIsolated().map - { - if (index + 1) % 4 == 0 { - // Let's not stack-overflow if the futures insta-complete which they at least in HTTP/2 - // mode. - // Also, we must frequently return to the EventLoop because we may get the pause signal - // from another thread. If we fail to do that promptly, we may balloon our body chunks - // into memory. - allDone.futureResult.eventLoop.assumeIsolated().execute { - writeNextChunk((offset: index, element: element), allDone: allDone) - } - } else { - writeNextChunk((offset: index, element: element), allDone: allDone) - } - }.nonisolated().cascadeFailure(to: allDone) - } else { - self.write(.byteBuffer(ByteBuffer(bytes: chunk.element))).cascade(to: allDone) - } - } - - let allDone = loop.makePromise(of: Void.self) - writeNextChunk(chunk, allDone: allDone) - return allDone.futureResult - } - } - } - - /// Body size. If nil,`Transfer-Encoding` will automatically be set to `chunked`. Otherwise a `Content-Length` - /// header is set with the given `length`. - @available(*, deprecated, renamed: "contentLength") - public var length: Int? { - get { - self.contentLength.flatMap { Int($0) } - } - set { - self.contentLength = newValue.flatMap { Int64($0) } - } - } - - /// Body size. If nil,`Transfer-Encoding` will automatically be set to `chunked`. Otherwise a `Content-Length` - /// header is set with the given `contentLength`. - public var contentLength: Int64? - - /// Body chunk provider. - public var stream: @Sendable (StreamWriter) -> EventLoopFuture - - @usableFromInline typealias StreamCallback = @Sendable (StreamWriter) -> EventLoopFuture - - @inlinable - init(contentLength: Int64?, stream: @escaping StreamCallback) { - self.contentLength = contentLength.flatMap { $0 } - self.stream = stream - } - - /// Create and stream body using `ByteBuffer`. - /// - /// - parameters: - /// - buffer: Body `ByteBuffer` representation. - public static func byteBuffer(_ buffer: ByteBuffer) -> Body { - Body(contentLength: Int64(buffer.readableBytes)) { writer in - writer.write(.byteBuffer(buffer)) - } - } - - /// Create and stream body using ``StreamWriter``. - /// - /// - parameters: - /// - length: Body size. If nil, `Transfer-Encoding` will automatically be set to `chunked`. Otherwise a `Content-Length` - /// header is set with the given `length`. - /// - stream: Body chunk provider. - @_disfavoredOverload - @preconcurrency - public static func stream( - length: Int? = nil, - _ stream: @Sendable @escaping (StreamWriter) -> EventLoopFuture - ) -> Body { - Body(contentLength: length.flatMap { Int64($0) }, stream: stream) - } - - /// Create and stream body using ``StreamWriter``. - /// - /// - parameters: - /// - contentLength: Body size. If nil, `Transfer-Encoding` will automatically be set to `chunked`. Otherwise a `Content-Length` - /// header is set with the given `contentLength`. - /// - stream: Body chunk provider. - public static func stream( - contentLength: Int64? = nil, - _ stream: @Sendable @escaping (StreamWriter) -> EventLoopFuture - ) -> Body { - Body(contentLength: contentLength, stream: stream) - } - - /// Create and stream body using a collection of bytes. - /// - /// - parameters: - /// - bytes: Body binary representation. - @preconcurrency - @inlinable - public static func bytes(_ bytes: Bytes) -> Body - where Bytes: RandomAccessCollection, Bytes: Sendable, Bytes.Element == UInt8 { - Body(contentLength: Int64(bytes.count)) { writer in - if bytes.count <= bagOfBytesToByteBufferConversionChunkSize { - return writer.write(.byteBuffer(ByteBuffer(bytes: bytes))) - } else { - return writer.writeChunks(of: bytes, maxChunkSize: bagOfBytesToByteBufferConversionChunkSize) - } - } - } - - /// Create and stream body using `String`. - /// - /// - parameters: - /// - string: Body `String` representation. - public static func string(_ string: String) -> Body { - Body(contentLength: Int64(string.utf8.count)) { writer in - if string.utf8.count <= bagOfBytesToByteBufferConversionChunkSize { - return writer.write(.byteBuffer(ByteBuffer(string: string))) - } else { - return writer.writeChunks(of: string.utf8, maxChunkSize: bagOfBytesToByteBufferConversionChunkSize) - } - } - } - } - - /// Represents an HTTP request. - public struct Request: Sendable { - /// Request HTTP method, defaults to `GET`. - public let method: HTTPMethod - /// Remote URL. - public let url: URL - - /// Remote HTTP scheme, resolved from `URL`. - public var scheme: String { - self.deconstructedURL.scheme.rawValue - } - - /// Request custom HTTP Headers, defaults to no headers. - public var headers: HTTPHeaders - /// Request body, defaults to no body. - public var body: Body? - /// Request-specific TLS configuration, defaults to no request-specific TLS configuration. - public var tlsConfiguration: TLSConfiguration? - - /// Parsed, validated and deconstructed URL. - let deconstructedURL: DeconstructedURL - - /// Create HTTP request. - /// - /// - parameters: - /// - url: Remote `URL`. - /// - method: HTTP method. - /// - headers: Custom HTTP headers. - /// - body: Request body. - /// - throws: - /// - `invalidURL` if URL cannot be parsed. - /// - `emptyScheme` if URL does not contain HTTP scheme. - /// - `unsupportedScheme` if URL does contains unsupported HTTP scheme. - /// - `emptyHost` if URL does not contains a host. - public init( - url: String, - method: HTTPMethod = .GET, - headers: HTTPHeaders = HTTPHeaders(), - body: Body? = nil - ) throws { - try self.init(url: url, method: method, headers: headers, body: body, tlsConfiguration: nil) - } - - /// Create HTTP request. - /// - /// - parameters: - /// - url: Remote `URL`. - /// - method: HTTP method. - /// - headers: Custom HTTP headers. - /// - body: Request body. - /// - tlsConfiguration: Request TLS configuration - /// - throws: - /// - `invalidURL` if URL cannot be parsed. - /// - `emptyScheme` if URL does not contain HTTP scheme. - /// - `unsupportedScheme` if URL does contains unsupported HTTP scheme. - /// - `emptyHost` if URL does not contains a host. - public init( - url: String, - method: HTTPMethod = .GET, - headers: HTTPHeaders = HTTPHeaders(), - body: Body? = nil, - tlsConfiguration: TLSConfiguration? - ) throws { - guard let url = URL(string: url) else { - throw HTTPClientError.invalidURL - } - - try self.init(url: url, method: method, headers: headers, body: body, tlsConfiguration: tlsConfiguration) - } - - /// Create an HTTP `Request`. - /// - /// - parameters: - /// - url: Remote `URL`. - /// - method: HTTP method. - /// - headers: Custom HTTP headers. - /// - body: Request body. - /// - throws: - /// - `emptyScheme` if URL does not contain HTTP scheme. - /// - `unsupportedScheme` if URL does contains unsupported HTTP scheme. - /// - `emptyHost` if URL does not contains a host. - /// - `missingSocketPath` if URL does not contains a socketPath as an encoded host. - public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws - { - try self.init(url: url, method: method, headers: headers, body: body, tlsConfiguration: nil) - } - - /// Create an HTTP `Request`. - /// - /// - parameters: - /// - url: Remote `URL`. - /// - method: HTTP method. - /// - headers: Custom HTTP headers. - /// - body: Request body. - /// - tlsConfiguration: Request TLS configuration - /// - throws: - /// - `emptyScheme` if URL does not contain HTTP scheme. - /// - `unsupportedScheme` if URL does contains unsupported HTTP scheme. - /// - `emptyHost` if URL does not contains a host. - /// - `missingSocketPath` if URL does not contains a socketPath as an encoded host. - public init( - url: URL, - method: HTTPMethod = .GET, - headers: HTTPHeaders = HTTPHeaders(), - body: Body? = nil, - tlsConfiguration: TLSConfiguration? - ) throws { - self.deconstructedURL = try DeconstructedURL(url: url) - - self.url = url - self.method = method - self.headers = headers - self.body = body - self.tlsConfiguration = tlsConfiguration - } - - /// Remote host, resolved from `URL`. - public var host: String { - self.deconstructedURL.connectionTarget.host ?? "" - } - - /// Resolved port. - public var port: Int { - self.deconstructedURL.connectionTarget.port ?? self.deconstructedURL.scheme.defaultPort - } - - /// Whether request will be executed using secure socket. - public var useTLS: Bool { self.deconstructedURL.scheme.usesTLS } - - func createRequestHead() throws -> (HTTPRequestHead, RequestFramingMetadata) { - var head = HTTPRequestHead( - version: .http1_1, - method: self.method, - uri: self.deconstructedURL.uri, - headers: self.headers - ) - - head.headers.addHostIfNeeded(for: self.deconstructedURL) - - let metadata = try head.headers.validateAndSetTransportFraming( - method: self.method, - bodyLength: .init(self.body) - ) - - return (head, metadata) - } - - /// Set basic auth for a request. - /// - /// - parameters: - /// - username: the username to authenticate with - /// - password: authentication password associated with the username - public mutating func setBasicAuth(username: String, password: String) { - self.headers.setBasicAuth(username: username, password: password) - } - } - - /// Represents an HTTP response. - public struct Response: Sendable { - /// Remote host of the request. - public var host: String - /// Response HTTP status. - public var status: HTTPResponseStatus - /// Response HTTP version. - public var version: HTTPVersion - /// Reponse HTTP headers. - public var headers: HTTPHeaders - /// Response body. - public var body: ByteBuffer? - /// The history of all requests and responses in redirect order. - public var history: [RequestResponse] - - /// The target URL (after redirects) of the response. - public var url: URL? { - self.history.last?.request.url - } - - /// Create HTTP `Response`. - /// - /// - parameters: - /// - host: Remote host of the request. - /// - status: Response HTTP status. - /// - headers: Reponse HTTP headers. - /// - body: Response body. - @available(*, deprecated, renamed: "init(host:status:version:headers:body:)") - public init(host: String, status: HTTPResponseStatus, headers: HTTPHeaders, body: ByteBuffer?) { - self.host = host - self.status = status - self.version = HTTPVersion(major: 1, minor: 1) - self.headers = headers - self.body = body - self.history = [] - } - - /// Create HTTP `Response`. - /// - /// - parameters: - /// - host: Remote host of the request. - /// - status: Response HTTP status. - /// - version: Response HTTP version. - /// - headers: Reponse HTTP headers. - /// - body: Response body. - public init( - host: String, - status: HTTPResponseStatus, - version: HTTPVersion, - headers: HTTPHeaders, - body: ByteBuffer? - ) { - self.host = host - self.status = status - self.version = version - self.headers = headers - self.body = body - self.history = [] - } - - /// Create HTTP `Response`. - /// - /// - parameters: - /// - host: Remote host of the request. - /// - status: Response HTTP status. - /// - version: Response HTTP version. - /// - headers: Reponse HTTP headers. - /// - body: Response body. - /// - history: History of all requests and responses in redirect order. - public init( - host: String, - status: HTTPResponseStatus, - version: HTTPVersion, - headers: HTTPHeaders, - body: ByteBuffer?, - history: [RequestResponse] - ) { - self.host = host - self.status = status - self.version = version - self.headers = headers - self.body = body - self.history = history - } - } - - /// HTTP authentication. - public struct Authorization: Hashable, Sendable { - private enum Scheme: Hashable { - case Basic(String) - case Bearer(String) - } - - private let scheme: Scheme - - private init(scheme: Scheme) { - self.scheme = scheme - } - - /// HTTP basic auth. - public static func basic(username: String, password: String) -> HTTPClient.Authorization { - .basic(credentials: Base64.encode(bytes: "\(username):\(password)".utf8)) - } - - /// HTTP basic auth. - /// - /// This version uses the raw string directly. - public static func basic(credentials: String) -> HTTPClient.Authorization { - .init(scheme: .Basic(credentials)) - } - - /// HTTP bearer auth - public static func bearer(tokens: String) -> HTTPClient.Authorization { - .init(scheme: .Bearer(tokens)) - } - - /// The header string for this auth field. - public var headerValue: String { - switch self.scheme { - case .Basic(let credentials): - return "Basic \(credentials)" - case .Bearer(let tokens): - return "Bearer \(tokens)" - } - } - } - - public struct RequestResponse: Sendable { - public var request: Request - public var responseHead: HTTPResponseHead - - public init(request: Request, responseHead: HTTPResponseHead) { - self.request = request - self.responseHead = responseHead - } - } -} - -/// The default ``HTTPClientResponseDelegate``. -/// -/// This ``HTTPClientResponseDelegate`` buffers a complete HTTP response in memory. It does not stream the response body in. -/// The resulting ``Response`` type is ``HTTPClient/Response``. -public final class ResponseAccumulator: HTTPClientResponseDelegate { - public typealias Response = HTTPClient.Response - - enum State { - case idle - case head(HTTPResponseHead) - case body(HTTPResponseHead, ByteBuffer) - case end - case error(Error) - } - - public struct ResponseTooBigError: Error, CustomStringConvertible { - public var maxBodySize: Int - public init(maxBodySize: Int) { - self.maxBodySize = maxBodySize - } - - public var description: String { - "ResponseTooBigError: received response body exceeds maximum accepted size of \(self.maxBodySize) bytes" - } - } - - private struct MutableState: Sendable { - var history = [HTTPClient.RequestResponse]() - var state = State.idle - } - - private let state: NIOLockedValueBox - let requestMethod: HTTPMethod - let requestHost: String - - static let maxByteBufferSize = Int(UInt32.max) - - /// Maximum size in bytes of the HTTP response body that ``ResponseAccumulator`` will accept - /// until it will abort the request and throw an ``ResponseTooBigError``. - /// - /// Default is 2^32. - /// - precondition: not allowed to exceed 2^32 because `ByteBuffer` can not store more bytes - public let maxBodySize: Int - - public convenience init(request: HTTPClient.Request) { - self.init(request: request, maxBodySize: Self.maxByteBufferSize) - } - - /// - Parameters: - /// - request: The corresponding request of the response this delegate will be accumulating. - /// - maxBodySize: Maximum size in bytes of the HTTP response body that ``ResponseAccumulator`` will accept - /// until it will abort the request and throw an ``ResponseTooBigError``. - /// Default is 2^32. - /// - precondition: maxBodySize is not allowed to exceed 2^32 because `ByteBuffer` can not store more bytes - /// - warning: You can use ``ResponseAccumulator`` for just one request. - /// If you start another request, you need to initiate another ``ResponseAccumulator``. - public init(request: HTTPClient.Request, maxBodySize: Int) { - precondition(maxBodySize >= 0, "maxBodyLength is not allowed to be negative") - precondition( - maxBodySize <= Self.maxByteBufferSize, - "maxBodyLength is not allowed to exceed 2^32 because ByteBuffer can not store more bytes" - ) - self.requestMethod = request.method - self.requestHost = request.host - self.maxBodySize = maxBodySize - self.state = NIOLockedValueBox(MutableState()) - } - - public func didVisitURL( - task: HTTPClient.Task, - _ request: HTTPClient.Request, - _ head: HTTPResponseHead - ) { - self.state.withLockedValue { - $0.history.append(.init(request: request, responseHead: head)) - } - } - - public func didReceiveHead(task: HTTPClient.Task, _ head: HTTPResponseHead) -> EventLoopFuture { - let responseTooBig: Bool - - if self.requestMethod != .HEAD, - let contentLength = head.headers.first(name: "Content-Length"), - let announcedBodySize = Int(contentLength), - announcedBodySize > self.maxBodySize - { - responseTooBig = true - } else { - responseTooBig = false - } - - return self.state.withLockedValue { - switch $0.state { - case .idle: - if responseTooBig { - let error = ResponseTooBigError(maxBodySize: self.maxBodySize) - $0.state = .error(error) - return task.eventLoop.makeFailedFuture(error) - } - - $0.state = .head(head) - case .head: - preconditionFailure("head already set") - case .body: - preconditionFailure("no head received before body") - case .end: - preconditionFailure("request already processed") - case .error: - break - } - return task.eventLoop.makeSucceededFuture(()) - } - } - - public func didReceiveBodyPart(task: HTTPClient.Task, _ part: ByteBuffer) -> EventLoopFuture { - self.state.withLockedValue { - switch $0.state { - case .idle: - preconditionFailure("no head received before body") - case .head(let head): - guard part.readableBytes <= self.maxBodySize else { - let error = ResponseTooBigError(maxBodySize: self.maxBodySize) - $0.state = .error(error) - return task.eventLoop.makeFailedFuture(error) - } - $0.state = .body(head, part) - case .body(let head, var body): - let newBufferSize = body.writerIndex + part.readableBytes - guard newBufferSize <= self.maxBodySize else { - let error = ResponseTooBigError(maxBodySize: self.maxBodySize) - $0.state = .error(error) - return task.eventLoop.makeFailedFuture(error) - } - - // The compiler can't prove that `self.state` is dead here (and it kinda isn't, there's - // a cross-module call in the way) so we need to drop the original reference to `body` in - // `self.state` or we'll get a CoW. To fix that we temporarily set the state to `.end` (which - // has no associated data). We'll fix it at the bottom of this block. - $0.state = .end - var part = part - body.writeBuffer(&part) - $0.state = .body(head, body) - case .end: - preconditionFailure("request already processed") - case .error: - break - } - return task.eventLoop.makeSucceededFuture(()) - } - } - - public func didReceiveError(task: HTTPClient.Task, _ error: Error) { - self.state.withLockedValue { - $0.state = .error(error) - } - } - - public func didFinishRequest(task: HTTPClient.Task) throws -> Response { - try self.state.withLockedValue { - switch $0.state { - case .idle: - preconditionFailure("no head received before end") - case .head(let head): - return Response( - host: self.requestHost, - status: head.status, - version: head.version, - headers: head.headers, - body: nil, - history: $0.history - ) - case .body(let head, let body): - return Response( - host: self.requestHost, - status: head.status, - version: head.version, - headers: head.headers, - body: body, - history: $0.history - ) - case .end: - preconditionFailure("request already processed") - case .error(let error): - throw error - } - } - } -} - -/// ``HTTPClientResponseDelegate`` allows an implementation to receive notifications about request processing and to control how response parts are processed. -/// -/// You can implement this protocol if you need fine-grained control over an HTTP request/response, for example, if you want to inspect the response -/// headers before deciding whether to accept a response body, or if you want to stream your request body. Pass an instance of your conforming -/// class to the ``HTTPClient/execute(request:delegate:eventLoop:deadline:)`` method and this package will call each delegate method appropriately as the request takes place. -/// -/// ### Backpressure -/// -/// A ``HTTPClientResponseDelegate`` can be used to exert backpressure on the server response. This is achieved by way of the futures returned from -/// ``HTTPClientResponseDelegate/didReceiveHead(task:_:)-9r4xd`` and ``HTTPClientResponseDelegate/didReceiveBodyPart(task:_:)-4fd4v``. -/// The following functions are part of the "backpressure system" in the delegate: -/// -/// - ``HTTPClientResponseDelegate/didReceiveHead(task:_:)-9r4xd`` -/// - ``HTTPClientResponseDelegate/didReceiveBodyPart(task:_:)-4fd4v`` -/// - ``HTTPClientResponseDelegate/didFinishRequest(task:)`` -/// - ``HTTPClientResponseDelegate/didReceiveError(task:_:)-fhsg`` -/// -/// The first three methods are strictly _exclusive_, with that exclusivity managed by the futures returned by ``HTTPClientResponseDelegate/didReceiveHead(task:_:)-9r4xd`` and -/// ``HTTPClientResponseDelegate/didReceiveBodyPart(task:_:)-4fd4v``. What this means is that until the returned future is completed, none of these three methods will be called -/// again. This allows delegates to rate limit the server to a capacity it can manage. ``HTTPClientResponseDelegate/didFinishRequest(task:)`` does not return a future, -/// as we are expecting no more data from the server at this time. -/// -/// ``HTTPClientResponseDelegate/didReceiveError(task:_:)-fhsg`` is somewhat special: it signals the end of this regime. ``HTTPClientResponseDelegate/didReceiveError(task:_:)-fhsg`` -/// is not exclusive: it may be called at any time, even if a returned future is not yet completed. ``HTTPClientResponseDelegate/didReceiveError(task:_:)-fhsg`` is terminal, meaning -/// that once it has been called none of these four methods will be called again. This can be used as a signal to abandon all outstanding work. -/// -/// - note: This delegate is strongly held by the `HTTPTaskHandler` -/// for the duration of the ``HTTPClient/Request`` processing and will be -/// released together with the `HTTPTaskHandler` when channel is closed. -/// Users of the library are not required to keep a reference to the -/// object that implements this protocol, but may do so if needed. -@preconcurrency -public protocol HTTPClientResponseDelegate: AnyObject, Sendable { - associatedtype Response: Sendable - - /// Called when the request head is sent. Will be called once. - /// - /// - parameters: - /// - task: Current request context. - /// - head: Request head. - func didSendRequestHead(task: HTTPClient.Task, _ head: HTTPRequestHead) - - /// Called when a part of the request body is sent. Could be called zero or more times. - /// - /// - parameters: - /// - task: Current request context. - /// - part: Request body part. - func didSendRequestPart(task: HTTPClient.Task, _ part: IOData) - - /// Called when the request is fully sent. Will be called once. - /// - /// - parameters: - /// - task: Current request context. - func didSendRequest(task: HTTPClient.Task) - - /// Called each time a response head is received (including redirects), and always called before ``HTTPClientResponseDelegate/didReceiveHead(task:_:)-9r4xd``. - /// You can use this method to keep an entire history of the request/response chain. - /// - /// - parameters: - /// - task: Current request context. - /// - request: The request that was sent. - /// - head: Received response head. - func didVisitURL(task: HTTPClient.Task, _ request: HTTPClient.Request, _ head: HTTPResponseHead) - - /// Called when the final response head is received (after redirects). - /// You must return an `EventLoopFuture` that you complete when you have finished processing the body part. - /// You can create an already succeeded future by calling `task.eventLoop.makeSucceededFuture(())`. - /// - /// - parameters: - /// - task: Current request context. - /// - head: Received reposonse head. - /// - returns: `EventLoopFuture` that will be used for backpressure. - func didReceiveHead(task: HTTPClient.Task, _ head: HTTPResponseHead) -> EventLoopFuture - - /// Called when part of a response body is received. Could be called zero or more times. - /// You must return an `EventLoopFuture` that you complete when you have finished processing the body part. - /// You can create an already succeeded future by calling `task.eventLoop.makeSucceededFuture(())`. - /// - /// This function will not be called until the future returned by ``HTTPClientResponseDelegate/didReceiveHead(task:_:)-9r4xd`` has completed. - /// - /// This function will not be called for subsequent body parts until the previous future returned by a - /// call to this function completes. - /// - /// - parameters: - /// - task: Current request context. - /// - buffer: Received body `Part`. - /// - returns: `EventLoopFuture` that will be used for backpressure. - func didReceiveBodyPart(task: HTTPClient.Task, _ buffer: ByteBuffer) -> EventLoopFuture - - /// Called when error was thrown during request execution. Will be called zero or one time only. Request processing will be stopped after that. - /// - /// This function may be called at any time: it does not respect the backpressure exerted by ``HTTPClientResponseDelegate/didReceiveHead(task:_:)-9r4xd`` - /// and ``HTTPClientResponseDelegate/didReceiveBodyPart(task:_:)-4fd4v``. - /// All outstanding work may be cancelled when this is received. Once called, no further calls will be made to - /// ``HTTPClientResponseDelegate/didReceiveHead(task:_:)-9r4xd``, ``HTTPClientResponseDelegate/didReceiveBodyPart(task:_:)-4fd4v``, - /// or ``HTTPClientResponseDelegate/didFinishRequest(task:)``. - /// - /// - parameters: - /// - task: Current request context. - /// - error: Error that occured during response processing. - func didReceiveError(task: HTTPClient.Task, _ error: Error) - - /// Called when the complete HTTP request is finished. You must return an instance of your ``Response`` associated type. Will be called once, except if an error occurred. - /// - /// This function will not be called until all futures returned by ``HTTPClientResponseDelegate/didReceiveHead(task:_:)-9r4xd`` and ``HTTPClientResponseDelegate/didReceiveBodyPart(task:_:)-4fd4v`` - /// have completed. Once called, no further calls will be made to ``HTTPClientResponseDelegate/didReceiveHead(task:_:)-9r4xd``, ``HTTPClientResponseDelegate/didReceiveBodyPart(task:_:)-4fd4v``, - /// or ``HTTPClientResponseDelegate/didReceiveError(task:_:)-fhsg``. - /// - /// - parameters: - /// - task: Current request context. - /// - returns: Result of processing. - func didFinishRequest(task: HTTPClient.Task) throws -> Response -} - -extension HTTPClientResponseDelegate { - /// Default implementation of ``HTTPClientResponseDelegate/didSendRequest(task:)-9od5p``. - /// - /// By default, this does nothing. - public func didSendRequestHead(task: HTTPClient.Task, _ head: HTTPRequestHead) {} - - /// Default implementation of ``HTTPClientResponseDelegate/didSendRequestPart(task:_:)-4qxap``. - /// - /// By default, this does nothing. - public func didSendRequestPart(task: HTTPClient.Task, _ part: IOData) {} - - /// Default implementation of ``HTTPClientResponseDelegate/didSendRequest(task:)-3vqgm``. - /// - /// By default, this does nothing. - public func didSendRequest(task: HTTPClient.Task) {} - - /// Default implementation of ``HTTPClientResponseDelegate/didVisitURL(task:_:_:)-2el9y``. - /// - /// By default, this does nothing. - public func didVisitURL(task: HTTPClient.Task, _: HTTPClient.Request, _: HTTPResponseHead) {} - - /// Default implementation of ``HTTPClientResponseDelegate/didReceiveHead(task:_:)-9r4xd``. - /// - /// By default, this does nothing. - public func didReceiveHead(task: HTTPClient.Task, _: HTTPResponseHead) -> EventLoopFuture { - task.eventLoop.makeSucceededVoidFuture() - } - - /// Default implementation of ``HTTPClientResponseDelegate/didReceiveBodyPart(task:_:)-4fd4v``. - /// - /// By default, this does nothing. - public func didReceiveBodyPart(task: HTTPClient.Task, _: ByteBuffer) -> EventLoopFuture { - task.eventLoop.makeSucceededVoidFuture() - } - - /// Default implementation of ``HTTPClientResponseDelegate/didReceiveError(task:_:)-fhsg``. - /// - /// By default, this does nothing. - public func didReceiveError(task: HTTPClient.Task, _: Error) {} -} - -extension URL { - var percentEncodedPath: String { - if self.path.isEmpty { - return "/" - } - return URLComponents(url: self, resolvingAgainstBaseURL: true)?.percentEncodedPath ?? self.path - } - - var uri: String { - var uri = self.percentEncodedPath - - if let query = self.query { - uri += "?" + query - } - - return uri - } - - func hasTheSameOrigin(as other: URL) -> Bool { - self.host == other.host && self.scheme == other.scheme && self.port == other.port - } - - /// Initializes a newly created HTTP URL connecting to a unix domain socket path. The socket path is encoded as the URL's host, replacing percent encoding invalid path characters, and will use the "http+unix" scheme. - /// - Parameters: - /// - socketPath: The path to the unix domain socket to connect to. - /// - uri: The URI path and query that will be sent to the server. - public init?(httpURLWithSocketPath socketPath: String, uri: String = "/") { - guard let host = socketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) else { return nil } - var urlString: String - if uri.hasPrefix("/") { - urlString = "http+unix://\(host)\(uri)" - } else { - urlString = "http+unix://\(host)/\(uri)" - } - self.init(string: urlString) - } - - /// Initializes a newly created HTTPS URL connecting to a unix domain socket path over TLS. The socket path is encoded as the URL's host, replacing percent encoding invalid path characters, and will use the "https+unix" scheme. - /// - Parameters: - /// - socketPath: The path to the unix domain socket to connect to. - /// - uri: The URI path and query that will be sent to the server. - public init?(httpsURLWithSocketPath socketPath: String, uri: String = "/") { - guard let host = socketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) else { return nil } - var urlString: String - if uri.hasPrefix("/") { - urlString = "https+unix://\(host)\(uri)" - } else { - urlString = "https+unix://\(host)/\(uri)" - } - self.init(string: urlString) - } -} - -protocol HTTPClientTaskDelegate: Sendable { - func fail(_ error: Error) -} - -extension HTTPClient { - /// Response execution context. - /// - /// Will be created by the library and could be used for obtaining - /// `EventLoopFuture` of the execution or cancellation of the execution. - public final class Task: Sendable { - /// The `EventLoop` the delegate will be executed on. - public let eventLoop: EventLoop - /// The `Logger` used by the `Task` for logging. - public let logger: Logger // We are okay to store the logger here because a Task is for only one request. - - let promise: EventLoopPromise - - struct State: Sendable { - var isCancelled: Bool - var taskDelegate: HTTPClientTaskDelegate? - } - - private let state: NIOLockedValueBox - - var isCancelled: Bool { - self.state.withLockedValue { $0.isCancelled } - } - - var taskDelegate: HTTPClientTaskDelegate? { - get { - self.state.withLockedValue { $0.taskDelegate } - } - set { - self.state.withLockedValue { $0.taskDelegate = newValue } - } - } - - private let makeOrGetFileIOThreadPool: @Sendable () -> NIOThreadPool - - /// The shared thread pool of a ``HTTPClient`` used for file IO. It is lazily created on first access. - internal var fileIOThreadPool: NIOThreadPool { - self.makeOrGetFileIOThreadPool() - } - - init(eventLoop: EventLoop, logger: Logger, makeOrGetFileIOThreadPool: @escaping @Sendable () -> NIOThreadPool) { - self.eventLoop = eventLoop - self.promise = eventLoop.makePromise() - self.logger = logger - self.makeOrGetFileIOThreadPool = makeOrGetFileIOThreadPool - self.state = NIOLockedValueBox(State(isCancelled: false, taskDelegate: nil)) - } - - static func failedTask( - eventLoop: EventLoop, - error: Error, - logger: Logger, - makeOrGetFileIOThreadPool: @escaping @Sendable () -> NIOThreadPool - ) -> Task { - let task = self.init( - eventLoop: eventLoop, - logger: logger, - makeOrGetFileIOThreadPool: makeOrGetFileIOThreadPool - ) - task.promise.fail(error) - return task - } - - /// `EventLoopFuture` for the response returned by this request. - public var futureResult: EventLoopFuture { - self.promise.futureResult - } - - /// Waits for execution of this request to complete. - /// - /// - returns: The value of ``futureResult`` when it completes. - /// - throws: The error value of ``futureResult`` if it errors. - @available(*, noasync, message: "wait() can block indefinitely, prefer get()", renamed: "get()") - @preconcurrency - public func wait() throws -> Response where Response: Sendable { - try self.promise.futureResult.wait() - } - - /// Provides the result of this request. - /// - /// - warning: This method may violates Structured Concurrency because doesn't respect cancellation. - /// - /// - returns: The value of ``futureResult`` when it completes. - /// - throws: The error value of ``futureResult`` if it errors. - @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) - @preconcurrency - public func get() async throws -> Response where Response: Sendable { - try await self.promise.futureResult.get() - } - - /// Initiate cancellation of a HTTP request. - /// - /// This method will return immeidately and doesn't wait for the cancellation to complete. - public func cancel() { - self.fail(reason: HTTPClientError.cancelled) - } - - /// Initiate cancellation of a HTTP request with an `error`. - /// - /// This method will return immeidately and doesn't wait for the cancellation to complete. - /// - /// - Parameter error: the error that is used to fail the promise - public func fail(reason error: Error) { - let taskDelegate = self.state.withLockedValue { state in - state.isCancelled = true - return state.taskDelegate - } - - taskDelegate?.fail(error) - } - - /// Called internally only, used to fail a task from within the state machine functionality. - func failInternal( - with error: Error - ) { - self.promise.fail(error) - } - } -} - -internal struct TaskCancelEvent {} - -// MARK: - RedirectHandler - -internal struct RedirectHandler { - let request: HTTPClient.Request - let redirectState: RedirectState - let execute: (HTTPClient.Request, RedirectState) -> HTTPClient.Task - - func redirectTarget(status: HTTPResponseStatus, responseHeaders: HTTPHeaders) -> URL? { - responseHeaders.extractRedirectTarget( - status: status, - originalURL: self.request.url, - originalScheme: self.request.deconstructedURL.scheme - ) - } - - func redirect( - status: HTTPResponseStatus, - to redirectURL: URL, - promise: EventLoopPromise - ) -> HTTPClient.Task? { - do { - var redirectState = self.redirectState - try redirectState.redirect(to: redirectURL.absoluteString) - - let (method, headers, body) = transformRequestForRedirect( - from: request.url, - method: self.request.method, - headers: self.request.headers, - body: self.request.body, - to: redirectURL, - status: status - ) - - let newRequest = try HTTPClient.Request( - url: redirectURL, - method: method, - headers: headers, - body: body - ) - - let newTask = self.execute(newRequest, redirectState) - - newTask.futureResult.whenComplete { result in - promise.futureResult.eventLoop.execute { - promise.completeWith(result) - } - } - - return newTask - } catch { - promise.fail(error) - return nil - } - } -} - -extension RequestBodyLength { - init(_ body: HTTPClient.Body?) { - guard let body = body else { - self = .known(0) - return - } - guard let length = body.contentLength else { - self = .unknown - return - } - self = .known(length) - } -} diff --git a/Sources/AsyncHTTPClient/LRUCache.swift b/Sources/AsyncHTTPClient/LRUCache.swift deleted file mode 100644 index f8b58c36a..000000000 --- a/Sources/AsyncHTTPClient/LRUCache.swift +++ /dev/null @@ -1,106 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -struct LRUCache { - private typealias Generation = UInt64 - private struct Element { - var generation: Generation - var key: Key - var value: Value - } - - private let capacity: Int - private var generation: Generation = 0 - private var elements: [Element] - - init(capacity: Int = 8) { - precondition(capacity > 0, "capacity needs to be > 0") - self.capacity = capacity - self.elements = [] - self.elements.reserveCapacity(capacity) - } - - private mutating func bumpGenerationAndFindIndex(key: Key) -> Int? { - self.generation += 1 - - let found = self.elements.firstIndex { element in - element.key == key - } - - return found - } - - mutating func find(key: Key) -> Value? { - if let found = self.bumpGenerationAndFindIndex(key: key) { - self.elements[found].generation = self.generation - return self.elements[found].value - } else { - return nil - } - } - - @discardableResult - mutating func append(key: Key, value: Value) -> Value { - let newElement = Element( - generation: self.generation, - key: key, - value: value - ) - if let found = self.bumpGenerationAndFindIndex(key: key) { - self.elements[found] = newElement - return value - } - - if self.elements.count < self.capacity { - self.elements.append(newElement) - return value - } - assert(self.elements.count == self.capacity) - assert(self.elements.count > 0) - - let minIndex = self.elements.minIndex { l, r in - l.generation < r.generation - }! - - self.elements.swapAt(minIndex, self.elements.endIndex - 1) - self.elements.removeLast() - self.elements.append(newElement) - - return value - } - - mutating func findOrAppend(key: Key, _ valueGenerator: (Key) -> Value) -> Value { - if let found = self.find(key: key) { - return found - } - - return self.append(key: key, value: valueGenerator(key)) - } -} - -extension Array { - func minIndex(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> Index? { - guard var minSoFar: (Index, Element) = self.first.map({ (0, $0) }) else { - return nil - } - - for indexElement in self.enumerated() { - if try areInIncreasingOrder(indexElement.1, minSoFar.1) { - minSoFar = indexElement - } - } - - return minSoFar.0 - } -} diff --git a/Sources/AsyncHTTPClient/NIOLoopBound+Execute.swift b/Sources/AsyncHTTPClient/NIOLoopBound+Execute.swift deleted file mode 100644 index b25a0f00d..000000000 --- a/Sources/AsyncHTTPClient/NIOLoopBound+Execute.swift +++ /dev/null @@ -1,28 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2025 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore - -extension NIOLoopBound { - @inlinable - func execute(_ body: @Sendable @escaping (Value) -> Void) { - if self.eventLoop.inEventLoop { - body(self.value) - } else { - self.eventLoop.execute { - body(self.value) - } - } - } -} diff --git a/Sources/AsyncHTTPClient/NIOTransportServices/NWErrorHandler.swift b/Sources/AsyncHTTPClient/NIOTransportServices/NWErrorHandler.swift deleted file mode 100644 index 148b4a4c4..000000000 --- a/Sources/AsyncHTTPClient/NIOTransportServices/NWErrorHandler.swift +++ /dev/null @@ -1,95 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2020 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOHTTP1 -import NIOTransportServices - -#if canImport(Network) -import Network -#endif - -extension HTTPClient { - #if canImport(Network) - /// A wrapper for `POSIX` errors thrown by `Network.framework`. - public struct NWPOSIXError: Error, CustomStringConvertible { - /// POSIX error code (enum) - public let errorCode: POSIXErrorCode - - /// actual reason, in human readable form - private let reason: String - - /// Initialise a NWPOSIXError - /// - Parameters: - /// - errorType: posix error type - /// - reason: String describing reason for error - public init(_ errorCode: POSIXErrorCode, reason: String) { - self.errorCode = errorCode - self.reason = reason - } - - public var description: String { self.reason } - } - - /// A wrapper for TLS errors thrown by `Network.framework`. - public struct NWTLSError: Error, CustomStringConvertible { - /// TLS error status. List of TLS errors can be found in `` - public let status: OSStatus - - /// actual reason, in human readable form - private let reason: String - - /// initialise a NWTLSError - /// - Parameters: - /// - status: TLS status - /// - reason: String describing reason for error - public init(_ status: OSStatus, reason: String) { - self.status = status - self.reason = reason - } - - public var description: String { self.reason } - } - #endif - - final class NWErrorHandler: ChannelInboundHandler { - typealias InboundIn = HTTPClientResponsePart - - func errorCaught(context: ChannelHandlerContext, error: Error) { - context.fireErrorCaught(NWErrorHandler.translateError(error)) - } - - static func translateError(_ error: Error) -> Error { - #if canImport(Network) - if #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) { - if let error = error as? NWError { - switch error { - case .tls(let status): - return NWTLSError(status, reason: String(describing: error)) - case .posix(let errorCode): - return NWPOSIXError(errorCode, reason: String(describing: error)) - default: - return error - } - } - return error - } else { - preconditionFailure("\(self) used on a non-NIOTS Channel") - } - #else - preconditionFailure("\(self) used on a non-NIOTS Channel") - #endif - } - } -} diff --git a/Sources/AsyncHTTPClient/NIOTransportServices/NWWaitingHandler.swift b/Sources/AsyncHTTPClient/NIOTransportServices/NWWaitingHandler.swift deleted file mode 100644 index d7c6055ec..000000000 --- a/Sources/AsyncHTTPClient/NIOTransportServices/NWWaitingHandler.swift +++ /dev/null @@ -1,44 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2022 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -#if canImport(Network) -import Network -import NIOCore -import NIOHTTP1 -import NIOTransportServices - -@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) -final class NWWaitingHandler: ChannelInboundHandler { - typealias InboundIn = Any - typealias InboundOut = Any - - private var requester: Requester - private let connectionID: HTTPConnectionPool.Connection.ID - - init(requester: Requester, connectionID: HTTPConnectionPool.Connection.ID) { - self.requester = requester - self.connectionID = connectionID - } - - func userInboundEventTriggered(context: ChannelHandlerContext, event: Any) { - if let waitingEvent = event as? NIOTSNetworkEvents.WaitingForConnectivity { - self.requester.waitingForConnectivity( - self.connectionID, - error: HTTPClient.NWErrorHandler.translateError(waitingEvent.transientError) - ) - } - context.fireUserInboundEventTriggered(event) - } -} -#endif diff --git a/Sources/AsyncHTTPClient/NIOTransportServices/TLSConfiguration.swift b/Sources/AsyncHTTPClient/NIOTransportServices/TLSConfiguration.swift deleted file mode 100644 index e8278e095..000000000 --- a/Sources/AsyncHTTPClient/NIOTransportServices/TLSConfiguration.swift +++ /dev/null @@ -1,221 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2020 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -#if canImport(Network) - -import Foundation -import Network -import NIOCore -import NIOSSL -import NIOTransportServices - -extension TLSVersion { - /// return Network framework TLS protocol version - @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) - var nwTLSProtocolVersion: tls_protocol_version_t { - switch self { - case .tlsv1: - return .TLSv10 - case .tlsv11: - return .TLSv11 - case .tlsv12: - return .TLSv12 - case .tlsv13: - return .TLSv13 - } - } -} - -extension TLSVersion { - /// return as SSL protocol - @available(macOS, deprecated: 10.15, message: "legacy functionality") - @available(iOS, deprecated: 13.0, message: "legacy functionality") - @available(tvOS, deprecated: 13.0, message: "legacy functionality") - @available(watchOS, deprecated: 6.0, message: "legacy functionality") - var sslProtocol: SSLProtocol { - switch self { - case .tlsv1: - return .tlsProtocol1 - case .tlsv11: - return .tlsProtocol11 - case .tlsv12: - return .tlsProtocol12 - case .tlsv13: - return .tlsProtocol13 - } - } -} - -@available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 5.0, *) -extension TLSConfiguration { - /// Dispatch queue used by Network framework TLS to control certificate verification - static let tlsDispatchQueue = DispatchQueue(label: "TLSDispatch") - - /// create NWProtocolTLS.Options for use with NIOTransportServices from the NIOSSL TLSConfiguration - /// - /// - Parameter eventLoop: EventLoop to wait for creation of options on - /// - Returns: Future holding NWProtocolTLS Options - func getNWProtocolTLSOptions( - on eventLoop: EventLoop, - serverNameIndicatorOverride: String? - ) -> EventLoopFuture { - let promise = eventLoop.makePromise(of: NWProtocolTLS.Options.self) - Self.tlsDispatchQueue.async { - do { - let options = try self.getNWProtocolTLSOptions(serverNameIndicatorOverride: serverNameIndicatorOverride) - promise.succeed(options) - } catch { - promise.fail(error) - } - } - return promise.futureResult - } - - /// create NWProtocolTLS.Options for use with NIOTransportServices from the NIOSSL TLSConfiguration - /// - /// - Returns: Equivalent NWProtocolTLS Options - func getNWProtocolTLSOptions(serverNameIndicatorOverride: String?) throws -> NWProtocolTLS.Options { - let options = NWProtocolTLS.Options() - - let useMTELGExplainer = """ - You can still use this configuration option on macOS if you initialize HTTPClient \ - with a MultiThreadedEventLoopGroup. Please note that using MultiThreadedEventLoopGroup \ - will make AsyncHTTPClient use NIO on BSD Sockets and not Network.framework (which is the preferred \ - platform networking stack). - """ - - if let serverNameIndicatorOverride = serverNameIndicatorOverride { - serverNameIndicatorOverride.withCString { serverNameIndicatorOverride in - sec_protocol_options_set_tls_server_name(options.securityProtocolOptions, serverNameIndicatorOverride) - } - } - - // minimum TLS protocol - if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { - sec_protocol_options_set_min_tls_protocol_version( - options.securityProtocolOptions, - self.minimumTLSVersion.nwTLSProtocolVersion - ) - } else { - sec_protocol_options_set_tls_min_version( - options.securityProtocolOptions, - self.minimumTLSVersion.sslProtocol - ) - } - - // maximum TLS protocol - if let maximumTLSVersion = self.maximumTLSVersion { - if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { - sec_protocol_options_set_max_tls_protocol_version( - options.securityProtocolOptions, - maximumTLSVersion.nwTLSProtocolVersion - ) - } else { - sec_protocol_options_set_tls_max_version(options.securityProtocolOptions, maximumTLSVersion.sslProtocol) - } - } - - // application protocols - for applicationProtocol in self.applicationProtocols { - applicationProtocol.withCString { buffer in - sec_protocol_options_add_tls_application_protocol(options.securityProtocolOptions, buffer) - } - } - - // cipher suites - if self.cipherSuites.count > 0 { - // TODO: Requires NIOSSL to provide list of cipher values before we can continue - // https://github.com/apple/swift-nio-ssl/issues/207 - } - - // key log callback - if self.keyLogCallback != nil { - preconditionFailure("TLSConfiguration.keyLogCallback is not supported. \(useMTELGExplainer)") - } - - // the certificate chain - if self.certificateChain.count > 0 { - preconditionFailure("TLSConfiguration.certificateChain is not supported. \(useMTELGExplainer)") - } - - // private key - if self.privateKey != nil { - preconditionFailure("TLSConfiguration.privateKey is not supported. \(useMTELGExplainer)") - } - - // renegotiation support key is unsupported - - // trust roots - var secTrustRoots: [SecCertificate]? - switch trustRoots { - case .some(.certificates(let certificates)): - secTrustRoots = try certificates.compactMap { certificate in - try SecCertificateCreateWithData(nil, Data(certificate.toDERBytes()) as CFData) - } - case .some(.file(let file)): - let certificates = try NIOSSLCertificate.fromPEMFile(file) - secTrustRoots = try certificates.compactMap { certificate in - try SecCertificateCreateWithData(nil, Data(certificate.toDERBytes()) as CFData) - } - - case .some(.default), .none: - break - } - - precondition( - self.certificateVerification != .noHostnameVerification, - "TLSConfiguration.certificateVerification = .noHostnameVerification is not supported. \(useMTELGExplainer)" - ) - - if certificateVerification != .fullVerification || trustRoots != nil { - // add verify block to control certificate verification - sec_protocol_options_set_verify_block( - options.securityProtocolOptions, - { _, sec_trust, sec_protocol_verify_complete in - guard self.certificateVerification != .none else { - sec_protocol_verify_complete(true) - return - } - - let trust = sec_trust_copy_ref(sec_trust).takeRetainedValue() - if let trustRootCertificates = secTrustRoots { - SecTrustSetAnchorCertificates(trust, trustRootCertificates as CFArray) - } - if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { - dispatchPrecondition(condition: .onQueue(Self.tlsDispatchQueue)) - SecTrustEvaluateAsyncWithError(trust, Self.tlsDispatchQueue) { _, result, error in - if let error = error { - print("Trust failed: \(error.localizedDescription)") - } - sec_protocol_verify_complete(result) - } - } else { - SecTrustEvaluateAsync(trust, Self.tlsDispatchQueue) { _, result in - switch result { - case .proceed, .unspecified: - sec_protocol_verify_complete(true) - default: - sec_protocol_verify_complete(false) - } - } - } - }, - Self.tlsDispatchQueue - ) - } - return options - } -} - -#endif diff --git a/Sources/AsyncHTTPClient/RedirectState.swift b/Sources/AsyncHTTPClient/RedirectState.swift deleted file mode 100644 index 95de2d508..000000000 --- a/Sources/AsyncHTTPClient/RedirectState.swift +++ /dev/null @@ -1,143 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOHTTP1 - -import struct Foundation.URL - -typealias RedirectMode = HTTPClient.Configuration.RedirectConfiguration.Mode - -struct RedirectState { - /// number of redirects we are allowed to follow. - private var limit: Int - - /// All visited URLs. - private var visited: [String] - - /// if true, `redirect(to:)` will throw an error if a cycle is detected. - private let allowCycles: Bool -} - -extension RedirectState { - /// Creates a `RedirectState` from a configuration. - /// Returns nil if the user disallowed redirects, - /// otherwise an instance of `RedirectState` which respects the user defined settings. - init?( - _ configuration: RedirectMode, - initialURL: String - ) { - switch configuration { - case .disallow: - return nil - case .follow(let maxRedirects, let allowCycles): - self.init(limit: maxRedirects, visited: [initialURL], allowCycles: allowCycles) - } - } -} - -extension RedirectState { - /// Call this method when you are about to do a redirect to the given `redirectURL`. - /// This method records that URL into `self`. - /// - Parameter redirectURL: the new URL to redirect the request to - /// - Throws: if it reaches the redirect limit or detects a redirect cycle if and `allowCycles` is false - mutating func redirect(to redirectURL: String) throws { - guard self.visited.count <= limit else { - throw HTTPClientError.redirectLimitReached - } - - guard allowCycles || !self.visited.contains(redirectURL) else { - throw HTTPClientError.redirectCycleDetected - } - self.visited.append(redirectURL) - } -} - -extension HTTPHeaders { - /// Tries to extract a redirect URL from the `location` header if the `status` indicates it should do so. - /// It also validates that we can redirect to the scheme of the extracted redirect URL from the `originalScheme`. - /// - Parameters: - /// - status: response status of the request - /// - originalURL: url of the previous request - /// - originalScheme: scheme of the previous request - /// - Returns: redirect URL to follow - func extractRedirectTarget( - status: HTTPResponseStatus, - originalURL: URL, - originalScheme: Scheme - ) -> URL? { - switch status { - case .movedPermanently, .found, .seeOther, .notModified, .useProxy, .temporaryRedirect, .permanentRedirect: - break - default: - return nil - } - - guard let location = self.first(name: "Location") else { - return nil - } - - guard let url = URL(string: location, relativeTo: originalURL) else { - return nil - } - - guard originalScheme.supportsRedirects(to: url.scheme) else { - return nil - } - - if url.isFileURL { - return nil - } - - return url.absoluteURL - } -} - -/// Transforms the original `requestMethod`, `requestHeaders` and `requestBody` to be ready to be send out as a new request to the `redirectURL`. -/// - Returns: New `HTTPMethod`, `HTTPHeaders` and `Body` to be send as a new request to `redirectURL` -func transformRequestForRedirect( - from originalURL: URL, - method requestMethod: HTTPMethod, - headers requestHeaders: HTTPHeaders, - body requestBody: Body?, - to redirectURL: URL, - status responseStatus: HTTPResponseStatus -) -> (HTTPMethod, HTTPHeaders, Body?) { - let convertToGet: Bool - if responseStatus == .seeOther, requestMethod != .HEAD { - convertToGet = true - } else if responseStatus == .movedPermanently || responseStatus == .found, requestMethod == .POST { - convertToGet = true - } else { - convertToGet = false - } - - var method = requestMethod - var headers = requestHeaders - var body = requestBody - - if convertToGet { - method = .GET - body = nil - headers.remove(name: "Content-Length") - headers.remove(name: "Content-Type") - } - - if !originalURL.hasTheSameOrigin(as: redirectURL) { - headers.remove(name: "Origin") - headers.remove(name: "Cookie") - headers.remove(name: "Authorization") - headers.remove(name: "Proxy-Authorization") - } - return (method, headers, body) -} diff --git a/Sources/AsyncHTTPClient/RequestBag+StateMachine.swift b/Sources/AsyncHTTPClient/RequestBag+StateMachine.swift deleted file mode 100644 index 37b2a42f0..000000000 --- a/Sources/AsyncHTTPClient/RequestBag+StateMachine.swift +++ /dev/null @@ -1,658 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOHTTP1 - -import struct Foundation.URL - -extension HTTPClient { - /// The maximum body size allowed, before a redirect response is cancelled. 3KB. - /// - /// Why 3KB? We feel like this is a good compromise between potentially reusing the - /// connection in HTTP/1.1 mode (if we load all data from the redirect response we can - /// reuse the connection) and not being to wasteful in the amount of data that is thrown - /// away being transferred. - fileprivate static let maxBodySizeRedirectResponse = 1024 * 3 -} - -extension RequestBag { - struct StateMachine { - fileprivate enum State { - case initialized(RedirectHandler?) - case queued(HTTPRequestScheduler, RedirectHandler?) - /// if the deadline was exceeded while in the `.queued(_:)` state, - /// we wait until the request pool fails the request with a potential more descriptive error message, - /// if a connection failure has occured while the request was queued. - case deadlineExceededWhileQueued - case executing(HTTPRequestExecutor, RequestStreamState, ResponseStreamState) - case finished(error: Error?) - case redirected(HTTPRequestExecutor, RedirectHandler, Int, HTTPResponseHead, URL) - case modifying - } - - fileprivate enum RequestStreamState { - case initialized - case producing - case paused(EventLoopPromise?) - case finished - } - - fileprivate enum ResponseStreamState { - enum Next { - case askExecutorForMore - case error(Error) - case eof - } - - case initialized(RedirectHandler?) - case buffering(CircularBuffer, next: Next) - case waitingForRemote - } - - private var state: State - - init(redirectHandler: RedirectHandler?) { - self.state = .initialized(redirectHandler) - } - } -} - -extension RequestBag.StateMachine { - mutating func requestWasQueued(_ scheduler: HTTPRequestScheduler) { - guard case .initialized(let redirectHandler) = self.state else { - // There might be a race between `requestWasQueued` and `willExecuteRequest`: - // - // If the request is created and passed to the HTTPClient on thread A, it will move into - // the connection pool lock in thread A. If no connection is available, thread A will - // add the request to the waiters and leave the connection pool lock. - // `requestWasQueued` will be called outside the connection pool lock on thread A. - // However if thread B has a connection that becomes available and thread B enters the - // connection pool lock directly after thread A, the request will be immediately - // scheduled for execution on thread B. After the thread B has left the lock it will - // call `willExecuteRequest` directly after. - // - // Having an order in the connection pool lock, does not guarantee an order in calling: - // `requestWasQueued` and `willExecuteRequest`. - // - // For this reason we must check the state here... If we are not `.initialized`, we are - // already executing. - return - } - - self.state = .queued(scheduler, redirectHandler) - } - - enum WillExecuteRequestAction { - case cancelExecuter(HTTPRequestExecutor) - case failTaskAndCancelExecutor(Error, HTTPRequestExecutor) - case none - } - - mutating func willExecuteRequest(_ executor: HTTPRequestExecutor) -> WillExecuteRequestAction { - switch self.state { - case .initialized(let redirectHandler), .queued(_, let redirectHandler): - self.state = .executing(executor, .initialized, .initialized(redirectHandler)) - return .none - case .deadlineExceededWhileQueued: - let error: Error = HTTPClientError.deadlineExceeded - self.state = .finished(error: error) - return .failTaskAndCancelExecutor(error, executor) - case .finished(error: .some): - return .cancelExecuter(executor) - case .executing, .redirected, .finished(error: .none), .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - enum ResumeProducingAction { - case startWriter - case succeedBackpressurePromise(EventLoopPromise?) - case none - } - - mutating func resumeRequestBodyStream() -> ResumeProducingAction { - switch self.state { - case .initialized, .queued, .deadlineExceededWhileQueued: - preconditionFailure("A request stream can only be resumed, if the request was started") - - case .executing(let executor, .initialized, .initialized(let redirectHandler)): - self.state = .executing(executor, .producing, .initialized(redirectHandler)) - return .startWriter - - case .executing(_, .producing, _): - preconditionFailure("Expected that resume is only called when if we were paused before") - - case .executing(let executor, .paused(let promise), let responseState): - self.state = .executing(executor, .producing, responseState) - return .succeedBackpressurePromise(promise) - - case .executing(_, .finished, _): - // the channels writability changed to writable after we have forwarded all the - // request bytes. Can be ignored. - return .none - - case .executing(_, .initialized, .buffering), .executing(_, .initialized, .waitingForRemote): - preconditionFailure("Invalid states: Response can not be received before request") - - case .redirected: - // if we are redirected, we should cancel our request body stream anyway - return .none - - case .finished: - // If this task has been cancelled we may be in an error state. As a matter of - // defensive programming, we also tolerate receiving this notification if we've ended cleanly: - // while it shouldn't happen, nothing will go wrong if we just ignore it. - // All paths through this state machine should cancel our request body stream to get here anyway. - return .none - - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - mutating func pauseRequestBodyStream() { - switch self.state { - case .initialized, .queued, .deadlineExceededWhileQueued: - preconditionFailure("A request stream can only be paused, if the request was started") - case .executing(let executor, let requestState, let responseState): - switch requestState { - case .initialized: - preconditionFailure("Request stream must be started before it can be paused") - case .producing: - self.state = .executing(executor, .paused(nil), responseState) - case .paused: - preconditionFailure("Expected that pause is only called when if we were producing before") - case .finished: - // the channels writability changed to not writable after we have forwarded the - // last bytes from our side. - break - } - case .redirected: - // if we are redirected, we should cancel our request body stream anyway - break - case .finished: - // the request is already finished nothing further to do - break - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - enum WriteAction { - case write(IOData, HTTPRequestExecutor, EventLoopFuture) - - case failTask(Error) - case failFuture(Error) - } - - mutating func writeNextRequestPart(_ part: IOData, taskEventLoop: EventLoop) -> WriteAction { - switch self.state { - case .initialized, .queued, .deadlineExceededWhileQueued: - preconditionFailure("Invalid state: \(self.state)") - case .executing(let executor, let requestState, let responseState): - switch requestState { - case .initialized: - preconditionFailure("Request stream must be started before it can be paused") - case .producing: - return .write(part, executor, taskEventLoop.makeSucceededFuture(())) - - case .paused(.none): - // backpressure is signaled to the writer using unfulfilled futures. if there - // is no existing, unfulfilled promise, let's create a new one - let promise = taskEventLoop.makePromise(of: Void.self) - self.state = .executing(executor, .paused(promise), responseState) - return .write(part, executor, promise.futureResult) - - case .paused(.some(let promise)): - // backpressure is signaled to the writer using unfulfilled futures. if an - // unfulfilled promise already exist, let's reuse the promise - return .write(part, executor, promise.futureResult) - - case .finished: - let error = HTTPClientError.writeAfterRequestSent - self.state = .finished(error: error) - return .failTask(error) - } - case .redirected: - // if we are redirected we can cancel the upload stream - return .failFuture(HTTPClientError.cancelled) - case .finished(error: .some(let error)): - return .failFuture(error) - case .finished(error: .none): - return .failFuture(HTTPClientError.requestStreamCancelled) - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - enum FinishAction { - case forwardStreamFinished(HTTPRequestExecutor, EventLoopPromise?) - case forwardStreamFailureAndFailTask(HTTPRequestExecutor, Error, EventLoopPromise?) - case none - } - - mutating func finishRequestBodyStream(_ result: Result) -> FinishAction { - switch self.state { - case .initialized, .queued, .deadlineExceededWhileQueued: - preconditionFailure("Invalid state: \(self.state)") - case .executing(let executor, let requestState, let responseState): - switch requestState { - case .initialized: - preconditionFailure("Request stream must be started before it can be finished") - case .producing: - switch result { - case .success: - self.state = .executing(executor, .finished, responseState) - return .forwardStreamFinished(executor, nil) - case .failure(let error): - self.state = .finished(error: error) - return .forwardStreamFailureAndFailTask(executor, error, nil) - } - - case .paused(let promise): - switch result { - case .success: - self.state = .executing(executor, .finished, responseState) - return .forwardStreamFinished(executor, promise) - case .failure(let error): - self.state = .finished(error: error) - return .forwardStreamFailureAndFailTask(executor, error, promise) - } - - case .finished: - preconditionFailure("How can a finished request stream, be finished again?") - } - case .redirected: - return .none - case .finished(error: _): - return .none - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - enum ReceiveResponseHeadAction { - case none - case forwardResponseHead(HTTPResponseHead) - case signalBodyDemand(HTTPRequestExecutor) - case redirect(HTTPRequestExecutor, RedirectHandler, HTTPResponseHead, URL) - } - - /// The response head has been received. - /// - /// - Parameter head: The response' head - /// - Returns: Whether the response should be forwarded to the delegate. Will be `false` if the request follows a redirect. - mutating func receiveResponseHead(_ head: HTTPResponseHead) -> ReceiveResponseHeadAction { - switch self.state { - case .initialized, .queued, .deadlineExceededWhileQueued: - preconditionFailure("How can we receive a response, if the request hasn't started yet.") - case .executing(let executor, let requestState, let responseState): - guard case .initialized(let redirectHandler) = responseState else { - preconditionFailure("If we receive a response, we must not have received something else before") - } - - if let redirectHandler = redirectHandler, - let redirectURL = redirectHandler.redirectTarget( - status: head.status, - responseHeaders: head.headers - ) - { - // If we will redirect, we need to consume the response's body ASAP, to be able to - // reuse the existing connection. We will consume a response body, if the body is - // smaller than 3kb. - switch head.contentLength { - case .some(0...(HTTPClient.maxBodySizeRedirectResponse)), .none: - self.state = .redirected(executor, redirectHandler, 0, head, redirectURL) - return .signalBodyDemand(executor) - case .some: - self.state = .finished(error: HTTPClientError.cancelled) - return .redirect(executor, redirectHandler, head, redirectURL) - } - } else { - self.state = .executing(executor, requestState, .buffering(.init(), next: .askExecutorForMore)) - return .forwardResponseHead(head) - } - case .redirected: - preconditionFailure("This state can only be reached after we have received a HTTP head") - case .finished(error: .some): - return .none - case .finished(error: .none): - preconditionFailure("How can the request be finished without error, before receiving response head?") - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - enum ReceiveResponseBodyAction { - case none - case forwardResponsePart(ByteBuffer) - case signalBodyDemand(HTTPRequestExecutor) - case redirect(HTTPRequestExecutor, RedirectHandler, HTTPResponseHead, URL) - } - - mutating func receiveResponseBodyParts(_ buffer: CircularBuffer) -> ReceiveResponseBodyAction { - switch self.state { - case .initialized, .queued, .deadlineExceededWhileQueued: - preconditionFailure("How can we receive a response body part, if the request hasn't started yet.") - case .executing(_, _, .initialized): - preconditionFailure("If we receive a response body, we must have received a head before") - - case .executing(let executor, let requestState, .buffering(var currentBuffer, next: let next)): - guard case .askExecutorForMore = next else { - preconditionFailure( - "If we have received an error or eof before, why did we get another body part? Next: \(next)" - ) - } - - self.state = .modifying - if currentBuffer.isEmpty { - currentBuffer = buffer - } else { - currentBuffer.append(contentsOf: buffer) - } - self.state = .executing(executor, requestState, .buffering(currentBuffer, next: next)) - return .none - case .executing(let executor, let requestState, .waitingForRemote): - if buffer.count > 0 { - var buffer = buffer - let first = buffer.removeFirst() - self.state = .executing(executor, requestState, .buffering(buffer, next: .askExecutorForMore)) - return .forwardResponsePart(first) - } else { - return .none - } - case .redirected(let executor, let redirectHandler, var receivedBytes, let head, let redirectURL): - let partsLength = buffer.reduce(into: 0) { $0 += $1.readableBytes } - receivedBytes += partsLength - - if receivedBytes > HTTPClient.maxBodySizeRedirectResponse { - self.state = .finished(error: HTTPClientError.cancelled) - return .redirect(executor, redirectHandler, head, redirectURL) - } else { - self.state = .redirected(executor, redirectHandler, receivedBytes, head, redirectURL) - return .signalBodyDemand(executor) - } - - case .finished(error: .some): - return .none - case .finished(error: .none): - preconditionFailure("How can the request be finished without error, before receiving response head?") - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - enum ReceiveResponseEndAction { - case consume(ByteBuffer) - case redirect(RedirectHandler, HTTPResponseHead, URL) - case succeedRequest - case none - } - - mutating func succeedRequest(_ newChunks: CircularBuffer?) -> ReceiveResponseEndAction { - switch self.state { - case .initialized, .queued, .deadlineExceededWhileQueued: - preconditionFailure("How can we receive a response body part, if the request hasn't started yet.") - case .executing(_, _, .initialized): - preconditionFailure("If we receive a response body, we must have received a head before") - - case .executing(let executor, let requestState, .buffering(var buffer, next: let next)): - guard case .askExecutorForMore = next else { - preconditionFailure( - "If we have received an error or eof before, why did we get another body part? Next: \(next)" - ) - } - - if buffer.isEmpty, let newChunks = newChunks, !newChunks.isEmpty { - buffer = newChunks - } else if let newChunks = newChunks, !newChunks.isEmpty { - buffer.append(contentsOf: newChunks) - } - - self.state = .executing(executor, requestState, .buffering(buffer, next: .eof)) - return .none - - case .executing(let executor, let requestState, .waitingForRemote): - guard var newChunks = newChunks, !newChunks.isEmpty else { - self.state = .finished(error: nil) - return .succeedRequest - } - - let first = newChunks.removeFirst() - self.state = .executing(executor, requestState, .buffering(newChunks, next: .eof)) - return .consume(first) - - case .redirected(_, let redirectHandler, _, let head, let redirectURL): - self.state = .finished(error: nil) - return .redirect(redirectHandler, head, redirectURL) - - case .finished(error: .some): - return .none - - case .finished(error: .none): - preconditionFailure("How can the request be finished without error, before receiving response head?") - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } - - enum ConsumeAction { - case requestMoreFromExecutor(HTTPRequestExecutor) - case consume(ByteBuffer) - case finishStream - case failTask(Error, executorToCancel: HTTPRequestExecutor?) - case doNothing - } - - mutating func consumeMoreBodyData(resultOfPreviousConsume result: Result) -> ConsumeAction { - switch result { - case .success: - return self.consumeMoreBodyData() - case .failure(let error): - return self.failWithConsumptionError(error) - } - } - - private mutating func failWithConsumptionError(_ error: Error) -> ConsumeAction { - switch self.state { - case .initialized, .queued, .deadlineExceededWhileQueued: - preconditionFailure("Invalid state: \(self.state)") - case .executing(_, _, .initialized): - preconditionFailure( - "Invalid state: Must have received response head, before this method is called for the first time" - ) - - case .executing(_, _, .buffering(_, next: .error(let connectionError))): - // if an error was received from the connection, we fail the task with the one - // from the connection, since it happened first. - self.state = .finished(error: connectionError) - return .failTask(connectionError, executorToCancel: nil) - - case .executing(let executor, _, .buffering(_, _)): - self.state = .finished(error: error) - return .failTask(error, executorToCancel: executor) - - case .executing(_, _, .waitingForRemote): - preconditionFailure( - "Invalid state... We just returned from a consumption function. We can't already be waiting" - ) - - case .redirected: - preconditionFailure( - "Invalid state... Redirect don't call out to delegate functions. Thus we should never land here." - ) - - case .finished(error: .some): - // don't overwrite existing errors - return .doNothing - - case .finished(error: .none): - preconditionFailure( - "Invalid state... If no error occured, this must not be called, after the request was finished" - ) - - case .modifying: - preconditionFailure() - } - } - - private mutating func consumeMoreBodyData() -> ConsumeAction { - switch self.state { - case .initialized, .queued, .deadlineExceededWhileQueued: - preconditionFailure("Invalid state: \(self.state)") - - case .executing(_, _, .initialized): - preconditionFailure( - "Invalid state: Must have received response head, before this method is called for the first time" - ) - - case .executing(let executor, let requestState, .buffering(var buffer, next: .askExecutorForMore)): - self.state = .modifying - - if let byteBuffer = buffer.popFirst() { - self.state = .executing(executor, requestState, .buffering(buffer, next: .askExecutorForMore)) - return .consume(byteBuffer) - } - - // buffer is empty, wait for more - self.state = .executing(executor, requestState, .waitingForRemote) - return .requestMoreFromExecutor(executor) - - case .executing(let executor, let requestState, .buffering(var buffer, next: .eof)): - self.state = .modifying - - if let byteBuffer = buffer.popFirst() { - self.state = .executing(executor, requestState, .buffering(buffer, next: .eof)) - return .consume(byteBuffer) - } - - self.state = .finished(error: nil) - return .finishStream - - case .executing(_, _, .buffering(_, next: .error(let error))): - self.state = .finished(error: error) - return .failTask(error, executorToCancel: nil) - - case .executing(_, _, .waitingForRemote): - preconditionFailure( - "Invalid state... We just returned from a consumption function. We can't already be waiting" - ) - - case .redirected: - return .doNothing - - case .finished(error: .some): - return .doNothing - - case .finished(error: .none): - preconditionFailure( - "Invalid state... If no error occurred, this must not be called, after the request was finished" - ) - - case .modifying: - preconditionFailure() - } - } - - enum DeadlineExceededAction { - case cancelScheduler(HTTPRequestScheduler?) - case fail(FailAction) - } - - mutating func deadlineExceeded() -> DeadlineExceededAction { - switch self.state { - case .queued(let queuer, _): - /// We do not fail the request immediately because we want to give the scheduler a chance of throwing a better error message - /// We therefore depend on the scheduler failing the request after we cancel the request. - self.state = .deadlineExceededWhileQueued - return .cancelScheduler(queuer) - - case .initialized, - .deadlineExceededWhileQueued, - .executing, - .finished, - .redirected, - .modifying: - /// if we are not in the queued state, we can fail early by just calling down to `self.fail(_:)` - /// which does the appropriate state transition for us. - return .fail(self.fail(HTTPClientError.deadlineExceeded)) - } - } - - enum FailAction { - case failTask(Error, HTTPRequestScheduler?, HTTPRequestExecutor?) - case cancelExecutor(HTTPRequestExecutor) - case none - } - - mutating func fail(_ error: Error) -> FailAction { - switch self.state { - case .initialized: - self.state = .finished(error: error) - return .failTask(error, nil, nil) - case .queued(let queuer, _): - self.state = .finished(error: error) - return .failTask(error, queuer, nil) - case .executing(let executor, let requestState, .buffering(_, next: .eof)): - self.state = .executing(executor, requestState, .buffering(.init(), next: .error(error))) - return .cancelExecutor(executor) - case .executing(let executor, _, .buffering(_, next: .askExecutorForMore)): - self.state = .finished(error: error) - return .failTask(error, nil, executor) - case .executing(let executor, _, .buffering(_, next: .error(_))): - // this would override another error, let's keep the first one - return .cancelExecutor(executor) - case .executing(let executor, _, .initialized): - self.state = .finished(error: error) - return .failTask(error, nil, executor) - case .executing(let executor, _, .waitingForRemote): - self.state = .finished(error: error) - return .failTask(error, nil, executor) - case .redirected: - self.state = .finished(error: error) - return .failTask(error, nil, nil) - case .finished(.none): - // An error occurred after the request has finished. Ignore... - return .none - case .deadlineExceededWhileQueued: - let realError: Error = { - if (error as? HTTPClientError) == .cancelled { - /// if we just get a `HTTPClientError.cancelled` we can use the original cancellation reason - /// to give a more descriptive error to the user. - return HTTPClientError.deadlineExceeded - } else { - /// otherwise we already had an intermediate connection error which we should present to the user instead - return error - } - }() - self.state = .finished(error: realError) - return .failTask(realError, nil, nil) - case .finished(.some(_)): - // this might happen, if the stream consumer has failed... let's just drop the data - return .none - case .modifying: - preconditionFailure("Invalid state: \(self.state)") - } - } -} - -extension HTTPResponseHead { - var contentLength: Int? { - guard let header = self.headers.first(name: "content-length") else { - return nil - } - return Int(header) - } -} diff --git a/Sources/AsyncHTTPClient/RequestBag.swift b/Sources/AsyncHTTPClient/RequestBag.swift deleted file mode 100644 index f206325ee..000000000 --- a/Sources/AsyncHTTPClient/RequestBag.swift +++ /dev/null @@ -1,527 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOHTTP1 -import NIOSSL - -@preconcurrency -final class RequestBag: Sendable { - /// Defends against the call stack getting too large when consuming body parts. - /// - /// If the response body comes in lots of tiny chunks, we'll deliver those tiny chunks to users - /// one at a time. - private static var maxConsumeBodyPartStackDepth: Int { - 50 - } - - let poolKey: ConnectionPool.Key - - let task: HTTPClient.Task - var eventLoop: EventLoop { - self.task.eventLoop - } - - private let delegate: Delegate - - struct LoopBoundState: @unchecked Sendable { - // The 'StateMachine' *isn't* Sendable (it holds various objects which aren't). This type - // needs to be sendable so that we can construct a loop bound box off of the event loop - // to hold this state and then subsequently only access it from the event loop. This needs - // to happen so that the request bag can be constructed off of the event loop. If it's - // constructed on the event loop then there's a timing window between users issuing - // a request and calling shutdown where the underlying pool doesn't know about the request - // so the shutdown call may cancel it. - var request: HTTPClient.Request - var state: StateMachine - var consumeBodyPartStackDepth: Int - // if a redirect occurs, we store the task for it so we can propagate cancellation - var redirectTask: HTTPClient.Task? = nil - } - - private let loopBoundState: NIOLoopBoundBox - - // MARK: HTTPClientTask properties - - var logger: Logger { - self.task.logger - } - - let connectionDeadline: NIODeadline - - let requestOptions: RequestOptions - - let requestHead: HTTPRequestHead - let requestFramingMetadata: RequestFramingMetadata - - let eventLoopPreference: HTTPClient.EventLoopPreference - - let tlsConfiguration: TLSConfiguration? - - init( - request: HTTPClient.Request, - eventLoopPreference: HTTPClient.EventLoopPreference, - task: HTTPClient.Task, - redirectHandler: RedirectHandler?, - connectionDeadline: NIODeadline, - requestOptions: RequestOptions, - delegate: Delegate - ) throws { - self.poolKey = .init(request, dnsOverride: requestOptions.dnsOverride) - self.eventLoopPreference = eventLoopPreference - self.task = task - - let loopBoundState = LoopBoundState( - request: request, - state: StateMachine(redirectHandler: redirectHandler), - consumeBodyPartStackDepth: 0 - ) - self.loopBoundState = NIOLoopBoundBox.makeBoxSendingValue(loopBoundState, eventLoop: task.eventLoop) - self.connectionDeadline = connectionDeadline - self.requestOptions = requestOptions - self.delegate = delegate - - let (head, metadata) = try request.createRequestHead() - self.requestHead = head - self.requestFramingMetadata = metadata - - self.tlsConfiguration = request.tlsConfiguration - - self.task.taskDelegate = self - self.task.futureResult.whenComplete { _ in - self.task.taskDelegate = nil - } - } - - private func requestWasQueued0(_ scheduler: HTTPRequestScheduler) { - self.logger.debug("Request was queued (waiting for a connection to become available)") - self.loopBoundState.value.state.requestWasQueued(scheduler) - } - - // MARK: - Request - - - private func willExecuteRequest0(_ executor: HTTPRequestExecutor) { - let action = self.loopBoundState.value.state.willExecuteRequest(executor) - switch action { - case .cancelExecuter(let executor): - executor.cancelRequest(self) - case .failTaskAndCancelExecutor(let error, let executor): - self.delegate.didReceiveError(task: self.task, error) - self.task.failInternal(with: error) - executor.cancelRequest(self) - case .none: - break - } - } - - private func requestHeadSent0() { - self.delegate.didSendRequestHead(task: self.task, self.requestHead) - - if self.loopBoundState.value.request.body == nil { - self.delegate.didSendRequest(task: self.task) - } - } - - private func resumeRequestBodyStream0() { - let produceAction = self.loopBoundState.value.state.resumeRequestBodyStream() - - switch produceAction { - case .startWriter: - guard let body = self.loopBoundState.value.request.body else { - preconditionFailure("Expected to have a body, if the `HTTPRequestStateMachine` resume a request stream") - } - self.loopBoundState.value.request.body = nil - - let writer = HTTPClient.Body.StreamWriter { - self.writeNextRequestPart($0) - } - - body.stream(writer).hop(to: self.eventLoop).whenComplete { - self.finishRequestBodyStream($0) - } - - case .succeedBackpressurePromise(let promise): - promise?.succeed(()) - - case .none: - break - } - } - - private func pauseRequestBodyStream0() { - self.loopBoundState.value.state.pauseRequestBodyStream() - } - - private func writeNextRequestPart(_ part: IOData) -> EventLoopFuture { - if self.eventLoop.inEventLoop { - return self.writeNextRequestPart0(part) - } else { - return self.eventLoop.flatSubmit { - self.writeNextRequestPart0(part) - } - } - } - - private func writeNextRequestPart0(_ part: IOData) -> EventLoopFuture { - let action = self.loopBoundState.value.state.writeNextRequestPart(part, taskEventLoop: self.task.eventLoop) - - switch action { - case .failTask(let error): - self.delegate.didReceiveError(task: self.task, error) - self.task.failInternal(with: error) - return self.task.eventLoop.makeFailedFuture(error) - - case .failFuture(let error): - return self.task.eventLoop.makeFailedFuture(error) - - case .write(let part, let writer, let future): - let promise = self.task.eventLoop.makePromise(of: Void.self) - promise.futureResult.whenSuccess { - self.delegate.didSendRequestPart(task: self.task, part) - } - writer.writeRequestBodyPart(part, request: self, promise: promise) - return future - } - } - - private func finishRequestBodyStream(_ result: Result) { - let action = self.loopBoundState.value.state.finishRequestBodyStream(result) - - switch action { - case .none: - break - case .forwardStreamFinished(let writer, let writerPromise): - let promise = writerPromise ?? self.task.eventLoop.makePromise(of: Void.self) - promise.futureResult.whenSuccess { - self.delegate.didSendRequest(task: self.task) - } - writer.finishRequestBodyStream(self, promise: promise) - - case .forwardStreamFailureAndFailTask(let writer, let error, let promise): - writer.cancelRequest(self) - promise?.fail(error) - self.failTask0(error) - } - } - - // MARK: Request delegate calls - - func failTask0(_ error: Error) { - self.task.eventLoop.assertInEventLoop() - - self.delegate.didReceiveError(task: self.task, error) - self.task.promise.fail(error) - } - - // MARK: - Response - - - private func receiveResponseHead0(_ head: HTTPResponseHead) { - self.delegate.didVisitURL(task: self.task, self.loopBoundState.value.request, head) - - // runs most likely on channel eventLoop - switch self.loopBoundState.value.state.receiveResponseHead(head) { - case .none: - break - - case .signalBodyDemand(let executor): - executor.demandResponseBodyStream(self) - - case .redirect(let executor, let handler, let head, let newURL): - self.loopBoundState.value.redirectTask = handler.redirect( - status: head.status, - to: newURL, - promise: self.task.promise - ) - executor.cancelRequest(self) - - case .forwardResponseHead(let head): - self.delegate.didReceiveHead(task: self.task, head) - .hop(to: self.task.eventLoop) - .whenComplete { result in - // After the head received, let's start to consume body data - self.consumeMoreBodyData0(resultOfPreviousConsume: result) - } - } - } - - private func receiveResponseBodyParts0(_ buffer: CircularBuffer) { - switch self.loopBoundState.value.state.receiveResponseBodyParts(buffer) { - case .none: - break - - case .signalBodyDemand(let executor): - executor.demandResponseBodyStream(self) - - case .redirect(let executor, let handler, let head, let newURL): - self.loopBoundState.value.redirectTask = handler.redirect( - status: head.status, - to: newURL, - promise: self.task.promise - ) - executor.cancelRequest(self) - - case .forwardResponsePart(let part): - self.delegate.didReceiveBodyPart(task: self.task, part) - .hop(to: self.task.eventLoop) - .whenComplete { result in - // on task el - self.consumeMoreBodyData0(resultOfPreviousConsume: result) - } - } - } - - private func succeedRequest0(_ buffer: CircularBuffer?) { - let action = self.loopBoundState.value.state.succeedRequest(buffer) - - switch action { - case .none: - break - case .consume(let buffer): - self.delegate.didReceiveBodyPart(task: self.task, buffer) - .hop(to: self.task.eventLoop) - .whenComplete { - self.consumeMoreBodyData0(resultOfPreviousConsume: $0) - } - - case .succeedRequest: - do { - let response = try self.delegate.didFinishRequest(task: self.task) - self.task.promise.succeed(response) - } catch { - self.task.promise.fail(error) - } - - case .redirect(let handler, let head, let newURL): - self.loopBoundState.value.redirectTask = handler.redirect( - status: head.status, - to: newURL, - promise: self.task.promise - ) - } - } - - private func consumeMoreBodyData0(resultOfPreviousConsume result: Result) { - // We get defensive here about the maximum stack depth. It's possible for the `didReceiveBodyPart` - // future to be returned to us completed. If it is, we will recurse back into this method. To - // break that recursion we have a max stack depth which we increment and decrement in this method: - // if it gets too large, instead of recurring we'll insert an `eventLoop.execute`, which will - // manually break the recursion and unwind the stack. - // - // Note that we don't bother starting this at the various other call sites that _begin_ stacks - // that risk ending up in this loop. That's because we don't need an accurate count: our limit is - // a best-effort target anyway, one stack frame here or there does not put us at risk. We're just - // trying to prevent ourselves looping out of control. - self.loopBoundState.value.consumeBodyPartStackDepth += 1 - defer { - self.loopBoundState.value.consumeBodyPartStackDepth -= 1 - assert(self.loopBoundState.value.consumeBodyPartStackDepth >= 0) - } - - let consumptionAction = self.loopBoundState.value.state.consumeMoreBodyData( - resultOfPreviousConsume: result - ) - - switch consumptionAction { - case .consume(let byteBuffer): - self.delegate.didReceiveBodyPart(task: self.task, byteBuffer) - .hop(to: self.task.eventLoop) - .assumeIsolated() - .whenComplete { result in - if self.loopBoundState.value.consumeBodyPartStackDepth < Self.maxConsumeBodyPartStackDepth { - self.consumeMoreBodyData0(resultOfPreviousConsume: result) - } else { - // We need to unwind the stack, let's take a break. - self.task.eventLoop.assumeIsolated().execute { - self.consumeMoreBodyData0(resultOfPreviousConsume: result) - } - } - } - - case .doNothing: - break - case .finishStream: - do { - let response = try self.delegate.didFinishRequest(task: self.task) - self.task.promise.assumeIsolated().succeed(response) - } catch { - self.task.promise.fail(error) - } - - case .failTask(let error, let executor): - executor?.cancelRequest(self) - self.failTask0(error) - case .requestMoreFromExecutor(let executor): - executor.demandResponseBodyStream(self) - } - } - - private func fail0(_ error: Error) { - let action = self.loopBoundState.value.state.fail(error) - - self.executeFailAction0(action) - - self.loopBoundState.value.redirectTask?.fail(reason: error) - } - - private func executeFailAction0(_ action: RequestBag.StateMachine.FailAction) { - switch action { - case .failTask(let error, let scheduler, let executor): - scheduler?.cancelRequest(self) - executor?.cancelRequest(self) - self.failTask0(error) - case .cancelExecutor(let executor): - executor.cancelRequest(self) - case .none: - break - } - } - - func deadlineExceeded0() { - let action = self.loopBoundState.value.state.deadlineExceeded() - - switch action { - case .cancelScheduler(let scheduler): - scheduler?.cancelRequest(self) - case .fail(let failAction): - self.executeFailAction0(failAction) - } - } - - func deadlineExceeded() { - if self.task.eventLoop.inEventLoop { - self.deadlineExceeded0() - } else { - self.task.eventLoop.execute { - self.deadlineExceeded0() - } - } - } -} - -extension RequestBag: HTTPSchedulableRequest, HTTPClientTaskDelegate { - - func requestWasQueued(_ scheduler: HTTPRequestScheduler) { - if self.task.eventLoop.inEventLoop { - self.requestWasQueued0(scheduler) - } else { - self.task.eventLoop.execute { - self.requestWasQueued0(scheduler) - } - } - } - - func fail(_ error: Error) { - if self.task.eventLoop.inEventLoop { - self.fail0(error) - } else { - self.task.eventLoop.execute { - self.fail0(error) - } - } - } -} - -extension RequestBag: HTTPExecutableRequest { - var requiredEventLoop: EventLoop? { - switch self.eventLoopPreference.preference { - case .indifferent, .delegate: - return nil - case .delegateAndChannel(on: let eventLoop), .testOnly_exact(channelOn: let eventLoop, delegateOn: _): - return eventLoop - } - } - - var preferredEventLoop: EventLoop { - switch self.eventLoopPreference.preference { - case .indifferent: - return self.task.eventLoop - case .delegate(let eventLoop), - .delegateAndChannel(on: let eventLoop), - .testOnly_exact(channelOn: let eventLoop, delegateOn: _): - return eventLoop - } - } - - func willExecuteRequest(_ executor: HTTPRequestExecutor) { - if self.task.eventLoop.inEventLoop { - self.willExecuteRequest0(executor) - } else { - self.task.eventLoop.execute { - self.willExecuteRequest0(executor) - } - } - } - - func requestHeadSent() { - if self.task.eventLoop.inEventLoop { - self.requestHeadSent0() - } else { - self.task.eventLoop.execute { - self.requestHeadSent0() - } - } - } - - func resumeRequestBodyStream() { - if self.task.eventLoop.inEventLoop { - self.resumeRequestBodyStream0() - } else { - self.task.eventLoop.execute { - self.resumeRequestBodyStream0() - } - } - } - - func pauseRequestBodyStream() { - if self.task.eventLoop.inEventLoop { - self.pauseRequestBodyStream0() - } else { - self.task.eventLoop.execute { - self.pauseRequestBodyStream0() - } - } - } - - func receiveResponseHead(_ head: HTTPResponseHead) { - if self.task.eventLoop.inEventLoop { - self.receiveResponseHead0(head) - } else { - self.task.eventLoop.execute { - self.receiveResponseHead0(head) - } - } - } - - func receiveResponseBodyParts(_ buffer: CircularBuffer) { - if self.task.eventLoop.inEventLoop { - self.receiveResponseBodyParts0(buffer) - } else { - self.task.eventLoop.execute { - self.receiveResponseBodyParts0(buffer) - } - } - } - - func succeedRequest(_ buffer: CircularBuffer?) { - if self.task.eventLoop.inEventLoop { - self.succeedRequest0(buffer) - } else { - self.task.eventLoop.execute { - self.succeedRequest0(buffer) - } - } - } -} diff --git a/Sources/AsyncHTTPClient/RequestValidation.swift b/Sources/AsyncHTTPClient/RequestValidation.swift deleted file mode 100644 index f338e06a9..000000000 --- a/Sources/AsyncHTTPClient/RequestValidation.swift +++ /dev/null @@ -1,181 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2018-2020 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOHTTP1 - -extension HTTPHeaders { - mutating func validateAndSetTransportFraming( - method: HTTPMethod, - bodyLength: RequestBodyLength - ) throws -> RequestFramingMetadata { - try self.validateFieldNames() - try self.validateFieldValues() - - if case .TRACE = method { - switch bodyLength { - case .known(0): - break - case .unknown, .known: - // A client MUST NOT send a message body in a TRACE request. - // https://tools.ietf.org/html/rfc7230#section-4.3.8 - throw HTTPClientError.traceRequestWithBody - } - } - - self.setTransportFraming(method: method, bodyLength: bodyLength) - - let connectionClose = self[canonicalForm: "connection"].lazy.map { $0.lowercased() }.contains("close") - switch bodyLength { - case .unknown: - return .init(connectionClose: connectionClose, body: .stream) - case .known(let length): - return .init(connectionClose: connectionClose, body: .fixedSize(length)) - } - } - - private func validateFieldNames() throws { - let invalidFieldNames = self.compactMap { name, _ -> String? in - let satisfy = name.utf8.allSatisfy { char -> Bool in - switch char { - case UInt8(ascii: "a")...UInt8(ascii: "z"), - UInt8(ascii: "A")...UInt8(ascii: "Z"), - UInt8(ascii: "0")...UInt8(ascii: "9"), - UInt8(ascii: "!"), - UInt8(ascii: "#"), - UInt8(ascii: "$"), - UInt8(ascii: "%"), - UInt8(ascii: "&"), - UInt8(ascii: "'"), - UInt8(ascii: "*"), - UInt8(ascii: "+"), - UInt8(ascii: "-"), - UInt8(ascii: "."), - UInt8(ascii: "^"), - UInt8(ascii: "_"), - UInt8(ascii: "`"), - UInt8(ascii: "|"), - UInt8(ascii: "~"): - return true - default: - return false - } - } - - return satisfy ? nil : name - } - - guard invalidFieldNames.count == 0 else { - throw HTTPClientError.invalidHeaderFieldNames(invalidFieldNames) - } - } - - private func validateFieldValues() throws { - let invalidValues = self.compactMap { _, value -> String? in - let satisfy = value.utf8.allSatisfy { char -> Bool in - /// Validates a byte of a given header field value against the definition in RFC 9110. - /// - /// The spec in [RFC 9110](https://httpwg.org/specs/rfc9110.html#fields.values) defines the valid - /// characters as the following: - /// - /// ``` - /// field-value = *field-content - /// field-content = field-vchar - /// [ 1*( SP / HTAB / field-vchar ) field-vchar ] - /// field-vchar = VCHAR / obs-text - /// obs-text = %x80-FF - /// ``` - /// - /// Additionally, it makes the following note: - /// - /// "Field values containing CR, LF, or NUL characters are invalid and dangerous, due to the - /// varying ways that implementations might parse and interpret those characters; a recipient - /// of CR, LF, or NUL within a field value MUST either reject the message or replace each of - /// those characters with SP before further processing or forwarding of that message. Field - /// values containing other CTL characters are also invalid; however, recipients MAY retain - /// such characters for the sake of robustness when they appear within a safe context (e.g., - /// an application-specific quoted string that will not be processed by any downstream HTTP - /// parser)." - /// - /// As we cannot guarantee the context is safe, this code will reject all ASCII control characters - /// directly _except_ for HTAB, which is explicitly allowed. - switch char { - case UInt8(ascii: "\t"): - // HTAB, explicitly allowed. - return true - case 0...0x1f, 0x7F: - // ASCII control character, forbidden. - return false - default: - // Printable or non-ASCII, allowed. - return true - } - } - - return satisfy ? nil : value - } - - guard invalidValues.count == 0 else { - throw HTTPClientError.invalidHeaderFieldValues(invalidValues) - } - } - - private mutating func setTransportFraming( - method: HTTPMethod, - bodyLength: RequestBodyLength - ) { - self.remove(name: "Content-Length") - self.remove(name: "Transfer-Encoding") - - switch bodyLength { - case .known(0): - // if we don't have a body we might not need to send the Content-Length field - // https://tools.ietf.org/html/rfc7230#section-3.3.2 - switch method { - case .GET, .HEAD, .DELETE, .CONNECT, .TRACE: - // A user agent SHOULD NOT send a Content-Length header field when the request - // message does not contain a payload body and the method semantics do not - // anticipate such a body. - break - default: - // A user agent SHOULD send a Content-Length in a request message when - // no Transfer-Encoding is sent and the request method defines a meaning - // for an enclosed payload body. - self.add(name: "Content-Length", value: "0") - } - case .known(let length): - self.add(name: "Content-Length", value: String(length)) - case .unknown: - self.add(name: "Transfer-Encoding", value: "chunked") - } - } -} - -extension HTTPHeaders { - mutating func addHostIfNeeded(for url: DeconstructedURL) { - // if no host header was set, let's use the url host - guard !self.contains(name: "host"), - var host = url.connectionTarget.host - else { - return - } - // if the request uses a non-default port, we need to add it after the host - if let port = url.connectionTarget.port, - port != url.scheme.defaultPort - { - host += ":\(port)" - } - self.add(name: "host", value: host) - } -} diff --git a/Sources/AsyncHTTPClient/SSLContextCache.swift b/Sources/AsyncHTTPClient/SSLContextCache.swift deleted file mode 100644 index 599003e56..000000000 --- a/Sources/AsyncHTTPClient/SSLContextCache.swift +++ /dev/null @@ -1,67 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Dispatch -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOSSL - -final class SSLContextCache { - private let lock = NIOLock() - private var sslContextCache = LRUCache() - private let offloadQueue = DispatchQueue(label: "io.github.swift-server.AsyncHTTPClient.SSLContextCache") -} - -extension SSLContextCache { - func sslContext( - tlsConfiguration: TLSConfiguration, - eventLoop: EventLoop, - logger: Logger - ) -> EventLoopFuture { - let eqTLSConfiguration = BestEffortHashableTLSConfiguration(wrapping: tlsConfiguration) - let sslContext = self.lock.withLock { - self.sslContextCache.find(key: eqTLSConfiguration) - } - - if let sslContext = sslContext { - logger.trace( - "found SSL context in cache", - metadata: ["ahc-tls-config": "\(tlsConfiguration)"] - ) - return eventLoop.makeSucceededFuture(sslContext) - } - - logger.trace( - "creating new SSL context", - metadata: ["ahc-tls-config": "\(tlsConfiguration)"] - ) - let newSSLContext = self.offloadQueue.asyncWithFuture(eventLoop: eventLoop) { - try NIOSSLContext(configuration: tlsConfiguration) - } - - newSSLContext.whenSuccess { (newSSLContext: NIOSSLContext) -> Void in - self.lock.withLock { () -> Void in - self.sslContextCache.append( - key: eqTLSConfiguration, - value: newSSLContext - ) - } - } - - return newSSLContext - } -} - -extension SSLContextCache: @unchecked Sendable {} diff --git a/Sources/AsyncHTTPClient/Scheme.swift b/Sources/AsyncHTTPClient/Scheme.swift deleted file mode 100644 index 16065a3c1..000000000 --- a/Sources/AsyncHTTPClient/Scheme.swift +++ /dev/null @@ -1,63 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -/// List of schemes `HTTPClient` currently supports -enum Scheme: String { - case http - case https - case unix - case httpUnix = "http+unix" - case httpsUnix = "https+unix" -} - -extension Scheme { - var usesTLS: Bool { - switch self { - case .http, .httpUnix, .unix: - return false - case .https, .httpsUnix: - return true - } - } - - var defaultPort: Int { - self.usesTLS ? 443 : 80 - } -} - -extension Scheme { - func supportsRedirects(to destinationScheme: String?) -> Bool { - guard - let destinationSchemeString = destinationScheme?.lowercased(), - let destinationScheme = Self(rawValue: destinationSchemeString) - else { - return false - } - return self.supportsRedirects(to: destinationScheme) - } - - func supportsRedirects(to destinationScheme: Self) -> Bool { - switch self { - case .http, .https: - switch destinationScheme { - case .http, .https: - return true - case .unix, .httpUnix, .httpsUnix: - return false - } - case .unix, .httpUnix, .httpsUnix: - return true - } - } -} diff --git a/Sources/AsyncHTTPClient/Singleton.swift b/Sources/AsyncHTTPClient/Singleton.swift deleted file mode 100644 index 0ddf1bc40..000000000 --- a/Sources/AsyncHTTPClient/Singleton.swift +++ /dev/null @@ -1,35 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2023 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -extension HTTPClient { - /// A globally shared, singleton ``HTTPClient``. - /// - /// The returned client uses the following settings: - /// - configuration is ``HTTPClient/Configuration/singletonConfiguration`` (matching the platform's default/prevalent browser as well as possible) - /// - `EventLoopGroup` is ``HTTPClient/defaultEventLoopGroup`` (matching the platform default) - /// - logging is disabled - public static var shared: HTTPClient { - globallySharedHTTPClient - } -} - -private let globallySharedHTTPClient: HTTPClient = { - let httpClient = HTTPClient( - eventLoopGroup: HTTPClient.defaultEventLoopGroup, - configuration: .singletonConfiguration, - backgroundActivityLogger: HTTPClient.loggingDisabled, - canBeShutDown: false - ) - return httpClient -}() diff --git a/Sources/AsyncHTTPClient/StringConvertibleInstances.swift b/Sources/AsyncHTTPClient/StringConvertibleInstances.swift deleted file mode 100644 index 61d4b067a..000000000 --- a/Sources/AsyncHTTPClient/StringConvertibleInstances.swift +++ /dev/null @@ -1,19 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2020-2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -extension HTTPClient.EventLoopPreference: CustomStringConvertible { - public var description: String { - "\(self.preference)" - } -} diff --git a/Sources/AsyncHTTPClient/StructuredConcurrencyHelpers.swift b/Sources/AsyncHTTPClient/StructuredConcurrencyHelpers.swift deleted file mode 100644 index 25f1225e0..000000000 --- a/Sources/AsyncHTTPClient/StructuredConcurrencyHelpers.swift +++ /dev/null @@ -1,83 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2025 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// -// swift-format-ignore -// Note: Whitespace changes are used to workaround compiler bug -// https://github.com/swiftlang/swift/issues/79285 - -#if compiler(>=6.0) -@inlinable -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -internal func asyncDo( - isolation: isolated (any Actor)? = #isolation, - // DO NOT FIX THE WHITESPACE IN THE NEXT LINE UNTIL 5.10 IS UNSUPPORTED - // https://github.com/swiftlang/swift/issues/79285 - _ body: () async throws -> sending R, finally: sending @escaping ((any Error)?) async throws -> Void) async throws -> sending R { - let result: R - do { - result = try await body() - } catch { - // `body` failed, we need to invoke `finally` with the `error`. - - // This _looks_ unstructured but isn't really because we unconditionally always await the return. - // We need to have an uncancelled task here to assure this is actually running in case we hit a - // cancellation error. - try await Task { - try await finally(error) - }.value - throw error - } - - // `body` succeeded, we need to invoke `finally` with `nil` (no error). - - // This _looks_ unstructured but isn't really because we unconditionally always await the return. - // We need to have an uncancelled task here to assure this is actually running in case we hit a - // cancellation error. - try await Task { - try await finally(nil) - }.value - return result -} -#else -@inlinable -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -internal func asyncDo( - _ body: () async throws -> R, - finally: @escaping @Sendable ((any Error)?) async throws -> Void -) async throws -> R { - let result: R - do { - result = try await body() - } catch { - // `body` failed, we need to invoke `finally` with the `error`. - - // This _looks_ unstructured but isn't really because we unconditionally always await the return. - // We need to have an uncancelled task here to assure this is actually running in case we hit a - // cancellation error. - try await Task { - try await finally(error) - }.value - throw error - } - - // `body` succeeded, we need to invoke `finally` with `nil` (no error). - - // This _looks_ unstructured but isn't really because we unconditionally always await the return. - // We need to have an uncancelled task here to assure this is actually running in case we hit a - // cancellation error. - try await Task { - try await finally(nil) - }.value - return result -} -#endif diff --git a/Sources/AsyncHTTPClient/Utils.swift b/Sources/AsyncHTTPClient/Utils.swift deleted file mode 100644 index 985755143..000000000 --- a/Sources/AsyncHTTPClient/Utils.swift +++ /dev/null @@ -1,73 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2018-2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore - -/// An ``HTTPClientResponseDelegate`` that wraps a callback. -/// -/// ``HTTPClientCopyingDelegate`` discards most parts of a HTTP response, but streams the body -/// to the `chunkHandler` provided on ``init(chunkHandler:)``. This is mostly useful for testing. -public final class HTTPClientCopyingDelegate: HTTPClientResponseDelegate, Sendable { - public typealias Response = Void - - let chunkHandler: @Sendable (ByteBuffer) -> EventLoopFuture - - @preconcurrency - public init(chunkHandler: @Sendable @escaping (ByteBuffer) -> EventLoopFuture) { - self.chunkHandler = chunkHandler - } - - public func didReceiveBodyPart(task: HTTPClient.Task, _ buffer: ByteBuffer) -> EventLoopFuture { - self.chunkHandler(buffer) - } - - public func didFinishRequest(task: HTTPClient.Task) throws { - () - } -} - -/// A utility function that runs the body code only in debug builds, without -/// emitting compiler warnings. -/// -/// This is currently the only way to do this in Swift: see -/// https://forums.swift.org/t/support-debug-only-code/11037 for a discussion. -@inlinable -internal func debugOnly(_ body: () -> Void) { - assert( - { - body() - return true - }() - ) -} - -extension BidirectionalCollection where Element: Equatable { - /// Returns a Boolean value indicating whether the collection ends with the specified suffix. - /// - /// If `suffix` is empty, this function returns `true`. - /// If all elements of the collections are equal, this function also returns `true`. - func hasSuffix(_ suffix: Suffix) -> Bool where Suffix: BidirectionalCollection, Suffix.Element == Element { - var ourIdx = self.endIndex - var suffixIdx = suffix.endIndex - while ourIdx > self.startIndex, suffixIdx > suffix.startIndex { - self.formIndex(before: &ourIdx) - suffix.formIndex(before: &suffixIdx) - guard self[ourIdx] == suffix[suffixIdx] else { return false } - } - guard suffixIdx == suffix.startIndex else { - return false // Exhausted self, but 'suffix' has elements remaining. - } - return true // Exhausted 'other' without finding a mismatch. - } -} diff --git a/Sources/CAsyncHTTPClient/CAsyncHTTPClient.c b/Sources/CAsyncHTTPClient/CAsyncHTTPClient.c deleted file mode 100644 index 6342da89f..000000000 --- a/Sources/CAsyncHTTPClient/CAsyncHTTPClient.c +++ /dev/null @@ -1,43 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2018-2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -#if __APPLE__ - #include -#elif __linux__ - #include -#endif - -#include -#include - -bool swiftahc_cshims_strptime(const char * string, const char * format, struct tm * result) { - const char * firstNonProcessed = strptime(string, format, result); - if (firstNonProcessed) { - return *firstNonProcessed == 0; - } - return false; -} - -bool swiftahc_cshims_strptime_l(const char * string, const char * format, struct tm * result, void * locale) { - // The pointer cast is fine as long we make sure it really points to a locale_t. -#if defined(__musl__) || defined(__ANDROID__) - const char * firstNonProcessed = strptime(string, format, result); -#else - const char * firstNonProcessed = strptime_l(string, format, result, (locale_t)locale); -#endif - if (firstNonProcessed) { - return *firstNonProcessed == 0; - } - return false; -} diff --git a/Sources/CAsyncHTTPClient/include/CAsyncHTTPClient.h b/Sources/CAsyncHTTPClient/include/CAsyncHTTPClient.h deleted file mode 100644 index 00aa87bb6..000000000 --- a/Sources/CAsyncHTTPClient/include/CAsyncHTTPClient.h +++ /dev/null @@ -1,34 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2018-2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -#ifndef CASYNC_HTTP_CLIENT_H -#define CASYNC_HTTP_CLIENT_H - -#include -#include - -bool swiftahc_cshims_strptime( - const char * _Nonnull input, - const char * _Nonnull format, - struct tm * _Nonnull result -); - -bool swiftahc_cshims_strptime_l( - const char * _Nonnull input, - const char * _Nonnull format, - struct tm * _Nonnull result, - void * _Nullable locale -); - -#endif // CASYNC_HTTP_CLIENT_H diff --git a/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift b/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift deleted file mode 100644 index 56a08b852..000000000 --- a/Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift +++ /dev/null @@ -1,1064 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOCore -import NIOFoundationCompat -import NIOHTTP1 -import NIOPosix -import NIOSSL -import XCTest - -@testable import AsyncHTTPClient - -private func makeDefaultHTTPClient( - eventLoopGroupProvider: HTTPClient.EventLoopGroupProvider = .singleton -) -> HTTPClient { - var config = HTTPClient.Configuration() - config.tlsConfiguration = .clientDefault - config.tlsConfiguration?.certificateVerification = .none - config.httpVersion = .automatic - return HTTPClient( - eventLoopGroupProvider: eventLoopGroupProvider, - configuration: config, - backgroundActivityLogger: Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - ) -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -final class AsyncAwaitEndToEndTests: XCTestCase { - var clientGroup: EventLoopGroup! - var serverGroup: EventLoopGroup! - - override func setUp() { - XCTAssertNil(self.clientGroup) - XCTAssertNil(self.serverGroup) - - self.clientGroup = getDefaultEventLoopGroup(numberOfThreads: 1) - self.serverGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - } - - override func tearDown() { - XCTAssertNotNil(self.clientGroup) - XCTAssertNoThrow(try self.clientGroup.syncShutdownGracefully()) - self.clientGroup = nil - - XCTAssertNotNil(self.serverGroup) - XCTAssertNoThrow(try self.serverGroup.syncShutdownGracefully()) - self.serverGroup = nil - } - - func testSimpleGet() { - XCTAsyncTest { - let bin = HTTPBin(.http2(compress: false)) - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - let request = HTTPClientRequest(url: "https://localhost/(bin.port)/get") - - guard - let response = await XCTAssertNoThrowWithResult( - try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) - ) - else { - return - } - - XCTAssertEqual(response.url?.absoluteString, request.url) - XCTAssertEqual(response.history.map(\.request.url), [request.url]) - XCTAssertEqual(response.status, .ok) - XCTAssertEqual(response.version, .http2) - } - } - - func testSimplePost() { - XCTAsyncTest { - let bin = HTTPBin(.http2(compress: false)) - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - let request = HTTPClientRequest(url: "https://localhost/(bin.port)/get") - - guard - let response = await XCTAssertNoThrowWithResult( - try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) - ) - else { - return - } - - XCTAssertEqual(response.url?.absoluteString, request.url) - XCTAssertEqual(response.history.map(\.request.url), [request.url]) - XCTAssertEqual(response.status, .ok) - XCTAssertEqual(response.version, .http2) - } - } - - func testPostWithByteBuffer() { - XCTAsyncTest { - let bin = HTTPBin(.http2(compress: false)) { _ in HTTPEchoHandler() } - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - var request = HTTPClientRequest(url: "https://localhost/(bin.port)/") - request.method = .POST - request.body = .bytes(ByteBuffer(string: "1234")) - - guard - let response = await XCTAssertNoThrowWithResult( - try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) - ) - else { return } - XCTAssertEqual(response.headers["content-length"], ["4"]) - guard - let body = await XCTAssertNoThrowWithResult( - try await response.body.collect(upTo: 1024) - ) - else { return } - XCTAssertEqual(body, ByteBuffer(string: "1234")) - } - } - - func testPostWithSequenceOfUInt8() { - XCTAsyncTest { - let bin = HTTPBin(.http2(compress: false)) { _ in HTTPEchoHandler() } - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - var request = HTTPClientRequest(url: "https://localhost/(bin.port)/") - request.method = .POST - request.body = .bytes(AnySendableSequence("1234".utf8), length: .unknown) - - guard - let response = await XCTAssertNoThrowWithResult( - try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) - ) - else { return } - XCTAssertEqual(response.headers["content-length"], []) - guard - let body = await XCTAssertNoThrowWithResult( - try await response.body.collect(upTo: 1024) - ) - else { return } - XCTAssertEqual(body, ByteBuffer(string: "1234")) - } - } - - func testPostWithCollectionOfUInt8() { - XCTAsyncTest { - let bin = HTTPBin(.http2(compress: false)) { _ in HTTPEchoHandler() } - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - var request = HTTPClientRequest(url: "https://localhost/(bin.port)/") - request.method = .POST - request.body = .bytes(AnySendableCollection("1234".utf8), length: .unknown) - - guard - let response = await XCTAssertNoThrowWithResult( - try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) - ) - else { return } - XCTAssertEqual(response.headers["content-length"], []) - guard - let body = await XCTAssertNoThrowWithResult( - try await response.body.collect(upTo: 1024) - ) - else { return } - XCTAssertEqual(body, ByteBuffer(string: "1234")) - } - } - - func testPostWithRandomAccessCollectionOfUInt8() { - XCTAsyncTest { - let bin = HTTPBin(.http2(compress: false)) { _ in HTTPEchoHandler() } - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - var request = HTTPClientRequest(url: "https://localhost/(bin.port)/") - request.method = .POST - request.body = .bytes(ByteBuffer(string: "1234").readableBytesView) - - guard - let response = await XCTAssertNoThrowWithResult( - try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) - ) - else { return } - XCTAssertEqual(response.headers["content-length"], ["4"]) - guard - let body = await XCTAssertNoThrowWithResult( - try await response.body.collect(upTo: 1024) - ) - else { return } - XCTAssertEqual(body, ByteBuffer(string: "1234")) - } - } - - struct AsyncSequenceByteBufferGenerator: AsyncSequence, Sendable, AsyncIteratorProtocol { - typealias Element = ByteBuffer - - let chunkSize: Int - let totalChunks: Int - let buffer: ByteBuffer - var chunksGenerated: Int = 0 - - init(chunkSize: Int, totalChunks: Int) { - self.chunkSize = chunkSize - self.totalChunks = totalChunks - self.buffer = ByteBuffer(repeating: 1, count: self.chunkSize) - } - - mutating func next() async throws -> ByteBuffer? { - guard self.chunksGenerated < self.totalChunks else { return nil } - - self.chunksGenerated += 1 - return self.buffer - } - - func makeAsyncIterator() -> AsyncSequenceByteBufferGenerator { - self - } - } - - func testEchoStreamThatHas3GBInTotal() async throws { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - let bin = HTTPBin(.http1_1()) { _ in HTTPEchoHandler() } - defer { XCTAssertNoThrow(try bin.shutdown()) } - - let client: HTTPClient = makeDefaultHTTPClient(eventLoopGroupProvider: .shared(eventLoopGroup)) - defer { XCTAssertNoThrow(try client.syncShutdown()) } - - let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - - var request = HTTPClientRequest(url: "http://localhost/(bin.port)/") - request.method = .POST - - let sequence = AsyncSequenceByteBufferGenerator( - chunkSize: 4_194_304, // 4MB chunk - totalChunks: 768 // Total = 3GB - ) - request.body = .stream(sequence, length: .unknown) - - let response: HTTPClientResponse = try await client.execute( - request, - deadline: .now() + .seconds(30), - logger: logger - ) - XCTAssertEqual(response.headers["content-length"], []) - - var receivedBytes: Int64 = 0 - for try await part in response.body { - receivedBytes += Int64(part.readableBytes) - } - XCTAssertEqual(receivedBytes, 3_221_225_472) // 3GB - } - - func testPostWithAsyncSequenceOfByteBuffers() { - XCTAsyncTest { - let bin = HTTPBin(.http2(compress: false)) { _ in HTTPEchoHandler() } - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - var request = HTTPClientRequest(url: "https://localhost/(bin.port)/") - request.method = .POST - request.body = .stream( - [ - ByteBuffer(string: "1"), - ByteBuffer(string: "2"), - ByteBuffer(string: "34"), - ].async, - length: .unknown - ) - - guard - let response = await XCTAssertNoThrowWithResult( - try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) - ) - else { return } - XCTAssertEqual(response.headers["content-length"], []) - guard - let body = await XCTAssertNoThrowWithResult( - try await response.body.collect(upTo: 1024) - ) - else { return } - XCTAssertEqual(body, ByteBuffer(string: "1234")) - } - } - - func testPostWithAsyncSequenceOfUInt8() { - XCTAsyncTest { - let bin = HTTPBin(.http2(compress: false)) { _ in HTTPEchoHandler() } - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - var request = HTTPClientRequest(url: "https://localhost/(bin.port)/") - request.method = .POST - request.body = .stream("1234".utf8.async, length: .unknown) - - guard - let response = await XCTAssertNoThrowWithResult( - try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) - ) - else { return } - XCTAssertEqual(response.headers["content-length"], []) - guard - let body = await XCTAssertNoThrowWithResult( - try await response.body.collect(upTo: 1024) - ) - else { return } - XCTAssertEqual(body, ByteBuffer(string: "1234")) - } - } - - func testPostWithFragmentedAsyncSequenceOfByteBuffers() { - XCTAsyncTest { - let bin = HTTPBin(.http2(compress: false)) { _ in HTTPEchoHandler() } - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - var request = HTTPClientRequest(url: "https://localhost/(bin.port)/") - request.method = .POST - let streamWriter = AsyncSequenceWriter() - request.body = .stream(streamWriter, length: .unknown) - - guard - let response = await XCTAssertNoThrowWithResult( - try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) - ) - else { return } - XCTAssertEqual(response.headers["content-length"], []) - - let fragments = [ - ByteBuffer(string: "1"), - ByteBuffer(string: "2"), - ByteBuffer(string: "34"), - ] - var bodyIterator = response.body.makeAsyncIterator() - for expectedFragment in fragments { - streamWriter.write(expectedFragment) - guard - let actualFragment = await XCTAssertNoThrowWithResult( - try await bodyIterator.next() - ) - else { return } - XCTAssertEqual(expectedFragment, actualFragment) - } - - streamWriter.end() - guard - let lastResult = await XCTAssertNoThrowWithResult( - try await bodyIterator.next() - ) - else { return } - XCTAssertEqual(lastResult, nil) - } - } - - func testPostWithFragmentedAsyncSequenceOfLargeByteBuffers() { - XCTAsyncTest { - let bin = HTTPBin(.http2(compress: false)) { _ in HTTPEchoHandler() } - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - var request = HTTPClientRequest(url: "https://localhost/(bin.port)/") - request.method = .POST - let streamWriter = AsyncSequenceWriter() - request.body = .stream(streamWriter, length: .unknown) - - guard - let response = await XCTAssertNoThrowWithResult( - try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) - ) - else { return } - XCTAssertEqual(response.headers["content-length"], []) - - let fragments = [ - ByteBuffer(string: String(repeating: "a", count: 4000)), - ByteBuffer(string: String(repeating: "b", count: 4000)), - ByteBuffer(string: String(repeating: "c", count: 4000)), - ByteBuffer(string: String(repeating: "d", count: 4000)), - ] - var bodyIterator = response.body.makeAsyncIterator() - for expectedFragment in fragments { - streamWriter.write(expectedFragment) - guard - let actualFragment = await XCTAssertNoThrowWithResult( - try await bodyIterator.next() - ) - else { return } - XCTAssertEqual(expectedFragment, actualFragment) - } - - streamWriter.end() - guard - let lastResult = await XCTAssertNoThrowWithResult( - try await bodyIterator.next() - ) - else { return } - XCTAssertEqual(lastResult, nil) - } - } - - func testCanceling() { - XCTAsyncTest(timeout: 5) { - let bin = HTTPBin(.http2(compress: false)) - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - var request = HTTPClientRequest(url: "http://localhost/(bin.port)/offline") - request.method = .POST - let streamWriter = AsyncSequenceWriter() - request.body = .stream(streamWriter, length: .unknown) - - let task = Task { [request] in - try await client.execute(request, deadline: .now() + .seconds(2), logger: logger) - } - task.cancel() - await XCTAssertThrowsError(try await task.value) { error in - XCTAssertTrue(error is CancellationError, "unexpected error \(error)") - } - } - } - - func testCancelingResponseBody() { - XCTAsyncTest(timeout: 5) { - let bin = HTTPBin(.http2(compress: false)) { _ in - HTTPEchoHandler() - } - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - var request = HTTPClientRequest(url: "https://localhost/(bin.port)/handler") - request.method = .POST - let streamWriter = AsyncSequenceWriter() - request.body = .stream(streamWriter, length: .unknown) - let response = try await client.execute(request, deadline: .now() + .seconds(2), logger: logger) - streamWriter.write(.init(bytes: [1])) - let task = Task { - try await response.body.collect(upTo: 1024 * 1024) - } - task.cancel() - - await XCTAssertThrowsError(try await task.value) { error in - XCTAssertTrue(error is CancellationError, "unexpected error \(error)") - } - - streamWriter.end() - } - } - - func testDeadline() { - XCTAsyncTest(timeout: 5) { - let bin = HTTPBin(.http2(compress: false)) - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - let request = HTTPClientRequest(url: "https://localhost/(bin.port)/wait") - - let task = Task { [request] in - try await client.execute(request, deadline: .now() + .milliseconds(100), logger: logger) - } - await XCTAssertThrowsError(try await task.value) { error in - guard let error = error as? HTTPClientError else { - return XCTFail("unexpected error \(error)") - } - // a race between deadline and connect timer can result in either error. - // If closing happens really fast we might shutdown the pipeline before we fail the request. - // If the pipeline is closed we may receive a `.remoteConnectionClosed`. - XCTAssertTrue( - [.deadlineExceeded, .connectTimeout, .remoteConnectionClosed].contains(error), - "unexpected error \(error)" - ) - } - } - } - - func testImmediateDeadline() { - XCTAsyncTest(timeout: 5) { - let bin = HTTPBin(.http2(compress: false)) - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - let request = HTTPClientRequest(url: "http://localhost/(bin.port)/wait") - - let task = Task { [request] in - try await client.execute(request, deadline: .now(), logger: logger) - } - await XCTAssertThrowsError(try await task.value) { error in - guard let error = error as? HTTPClientError else { - return XCTFail("unexpected error \(error)") - } - // a race between deadline and connect timer can result in either error. - // If closing happens really fast we might shutdown the pipeline before we fail the request. - // If the pipeline is closed we may receive a `.remoteConnectionClosed`. - XCTAssertTrue( - [.deadlineExceeded, .connectTimeout, .remoteConnectionClosed].contains(error), - "unexpected error \(error)" - ) - } - } - } - - func testConnectTimeout() { - let serverGroup = self.serverGroup! - let clientGroup = self.clientGroup! - XCTAsyncTest(timeout: 60) { - #if os(Linux) - // 198.51.100.254 is reserved for documentation only and therefore should not accept any TCP connection - let url = "http://198.51.100.254/get" - #else - // on macOS we can use the TCP backlog behaviour when the queue is full to simulate a non reachable server. - // this makes this test a bit more stable if `198.51.100.254` actually responds to connection attempt. - // The backlog behaviour on Linux can not be used to simulate a non-reachable server. - // Linux sends a `SYN/ACK` back even if the `backlog` queue is full as it has two queues. - // The second queue is not limit by `ChannelOptions.backlog` but by `/proc/sys/net/ipv4/tcp_max_syn_backlog`. - - let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { - XCTAssertNoThrow(try group.syncShutdownGracefully()) - } - - let serverChannel = try await ServerBootstrap(group: serverGroup) - .serverChannelOption(ChannelOptions.backlog, value: 1) - .serverChannelOption(ChannelOptions.autoRead, value: false) - .bind(host: "127.0.0.1", port: 0) - .get() - defer { - XCTAssertNoThrow(try serverChannel.close().wait()) - } - let port = serverChannel.localAddress!.port! - let firstClientChannel = try await ClientBootstrap(group: serverGroup) - .connect(host: "127.0.0.1", port: port) - .get() - defer { - XCTAssertNoThrow(try firstClientChannel.close().wait()) - } - let url = "http://localhost/(port)/get" - #endif - - let httpClient = HTTPClient( - eventLoopGroupProvider: .shared(clientGroup), - configuration: .init(timeout: .init(connect: .milliseconds(100), read: .milliseconds(150))) - ) - - defer { - XCTAssertNoThrow(try httpClient.syncShutdown()) - } - - let request = HTTPClientRequest(url: url) - let start = NIODeadline.now() - await XCTAssertThrowsError(try await httpClient.execute(request, deadline: .now() + .seconds(30))) { - XCTAssertEqualTypeAndValue($0, HTTPClientError.connectTimeout) - let end = NIODeadline.now() - let duration = end - start - - // We give ourselves 10x slack in order to be confident that even on slow machines this assertion passes. - // It's 30x smaller than our other timeout though. - XCTAssertLessThan(duration, .seconds(1)) - } - } - } - - func testSelfSignedCertificateIsRejectedWithCorrectErrorIfRequestDeadlineIsExceeded() { - XCTAsyncTest(timeout: 5) { - /// key + cert was created with the follwing command: - /// openssl req -x509 -newkey rsa:4096 -keyout self_signed_key.pem -out self_signed_cert.pem -sha256 -days 99999 -nodes -subj '/CN=localhost' - let certPath = Bundle.module.path(forResource: "self_signed_cert", ofType: "pem")! - let keyPath = Bundle.module.path(forResource: "self_signed_key", ofType: "pem")! - let key = try NIOSSLPrivateKey(file: keyPath, format: .pem) - let configuration = TLSConfiguration.makeServerConfiguration( - certificateChain: try NIOSSLCertificate.fromPEMFile(certPath).map { .certificate($0) }, - privateKey: .privateKey(key) - ) - let sslContext = try NIOSSLContext(configuration: configuration) - let serverGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try serverGroup.syncShutdownGracefully()) } - let server = ServerBootstrap(group: serverGroup) - .childChannelInitializer { channel in - channel.eventLoop.makeCompletedFuture { - try channel.pipeline.syncOperations.addHandler(NIOSSLServerHandler(context: sslContext)) - } - } - let serverChannel = try await server.bind(host: "localhost", port: 0).get() - defer { XCTAssertNoThrow(try serverChannel.close().wait()) } - let port = serverChannel.localAddress!.port! - - let config = HTTPClient.Configuration() - .enableFastFailureModeForTesting() - - let localClient = HTTPClient(eventLoopGroupProvider: .singleton, configuration: config) - defer { XCTAssertNoThrow(try localClient.syncShutdown()) } - let request = HTTPClientRequest(url: "https://localhost/(port)") - await XCTAssertThrowsError(try await localClient.execute(request, deadline: .now() + .seconds(2))) { - error in - #if canImport(Network) - guard let nwTLSError = error as? HTTPClient.NWTLSError else { - XCTFail("could not cast \(error) of type \(type(of: error)) to \(HTTPClient.NWTLSError.self)") - return - } - XCTAssertEqual(nwTLSError.status, errSSLBadCert, "unexpected tls error: \(nwTLSError)") - #else - guard let sslError = error as? NIOSSLError, - case .handshakeFailed(.sslError) = sslError - else { - XCTFail("unexpected error \(error)") - return - } - #endif - } - } - } - - func testDnsOverride() { - XCTAsyncTest(timeout: 5) { - // key + cert was created with the following code (depends on swift-certificates) - // ``` - // import X509 - // import CryptoKit - // import Foundation - // - // let privateKey = P384.Signing.PrivateKey() - // let name = try DistinguishedName { - // OrganizationName("Self Signed") - // CommonName("localhost") - // } - // let certificate = try Certificate( - // version: .v3, - // serialNumber: .init(), - // publicKey: .init(privateKey.publicKey), - // notValidBefore: Date(), - // notValidAfter: Date().advanced(by: 365 * 24 * 3600), - // issuer: name, - // subject: name, - // signatureAlgorithm: .ecdsaWithSHA384, - // extensions: try .init { - // SubjectAlternativeNames([.dnsName("example.com")]) - // try ExtendedKeyUsage([.serverAuth]) - // }, - // issuerPrivateKey: .init(privateKey) - // ) - // ``` - let certPath = Bundle.module.path(forResource: "example.com.cert", ofType: "pem")! - let keyPath = Bundle.module.path(forResource: "example.com.private-key", ofType: "pem")! - let key = try NIOSSLPrivateKey(file: keyPath, format: .pem) - let localhostCert = try NIOSSLCertificate.fromPEMFile(certPath) - let configuration = TLSConfiguration.makeServerConfiguration( - certificateChain: localhostCert.map { .certificate($0) }, - privateKey: .privateKey(key) - ) - let bin = HTTPBin(.http2(tlsConfiguration: configuration)) - defer { XCTAssertNoThrow(try bin.shutdown()) } - - var config = HTTPClient.Configuration() - .enableFastFailureModeForTesting() - var tlsConfig = TLSConfiguration.makeClientConfiguration() - - tlsConfig.trustRoots = .certificates(localhostCert) - config.tlsConfiguration = tlsConfig - // this is the actual configuration under test - config.dnsOverride = ["example.com": "localhost"] - - let localClient = HTTPClient(eventLoopGroupProvider: .singleton, configuration: config) - defer { XCTAssertNoThrow(try localClient.syncShutdown()) } - let request = HTTPClientRequest(url: "https://example.com/(bin.port)/echohostheader") - let response = await XCTAssertNoThrowWithResult( - try await localClient.execute(request, deadline: .now() + .seconds(2)) - ) - XCTAssertEqual(response?.status, .ok) - XCTAssertEqual(response?.version, .http2) - var body = try await response?.body.collect(upTo: 1024) - let readableBytes = body?.readableBytes ?? 0 - let responseInfo = try body?.readJSONDecodable(RequestInfo.self, length: readableBytes) - XCTAssertEqual(responseInfo?.data, "example.com\(bin.port == 443 ? "" : ":\(bin.port)")") - } - } - - func testInvalidURL() { - XCTAsyncTest(timeout: 5) { - let client = makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - let request = HTTPClientRequest(url: "") // invalid URL - - await XCTAssertThrowsError( - try await client.execute(request, deadline: .now() + .seconds(2), logger: logger) - ) { - XCTAssertEqual($0 as? HTTPClientError, .invalidURL) - } - } - } - - func testInsanelyHighConcurrentHTTP1ConnectionLimitDoesNotCrash() async throws { - let bin = HTTPBin(.http1_1(compress: false)) - defer { XCTAssertNoThrow(try bin.shutdown()) } - - var httpClientConfig = HTTPClient.Configuration() - httpClientConfig.connectionPool = .init( - idleTimeout: .hours(1), - concurrentHTTP1ConnectionsPerHostSoftLimit: Int.max - ) - httpClientConfig.timeout = .init(connect: .seconds(10), read: .seconds(100), write: .seconds(100)) - - let httpClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup), configuration: httpClientConfig) - defer { XCTAssertNoThrow(try httpClient.syncShutdown()) } - - let request = HTTPClientRequest(url: "http://localhost/(bin.port)") - _ = try await httpClient.execute(request, deadline: .now() + .seconds(2)) - } - - func testRedirectChangesHostHeader() { - XCTAsyncTest { - let bin = HTTPBin(.http2(compress: false)) - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - var request = HTTPClientRequest(url: "https://127.0.0.1/(bin.port)/redirect/target") - let redirectURL = "https://localhost/(bin.port)/echohostheader" - request.headers.replaceOrAdd( - name: "X-Target-Redirect-URL", - value: redirectURL - ) - - guard - let response = await XCTAssertNoThrowWithResult( - try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) - ) - else { - return - } - guard let body = await XCTAssertNoThrowWithResult(try await response.body.collect(upTo: 1024)) else { - return - } - var maybeRequestInfo: RequestInfo? - XCTAssertNoThrow(maybeRequestInfo = try JSONDecoder().decode(RequestInfo.self, from: body)) - guard let requestInfo = maybeRequestInfo else { return } - - XCTAssertEqual(response.url?.absoluteString, redirectURL) - XCTAssertEqual(response.history.map(\.request.url), [request.url, redirectURL]) - XCTAssertEqual(response.status, .ok) - XCTAssertEqual(response.version, .http2) - XCTAssertEqual(requestInfo.data, "localhost:\(bin.port)") - } - } - - func testShutdown() { - XCTAsyncTest { - let client = makeDefaultHTTPClient() - try await client.shutdown() - await XCTAssertThrowsError(try await client.shutdown()) { error in - XCTAssertEqualTypeAndValue(error, HTTPClientError.alreadyShutdown) - } - } - } - - /// Regression test for https://github.com/swift-server/async-http-client/issues/612 - func testCancelingBodyDoesNotCrash() { - XCTAsyncTest { - let client = makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let bin = HTTPBin(.http2(compress: true)) - defer { XCTAssertNoThrow(try bin.shutdown()) } - - let request = HTTPClientRequest(url: "https://127.0.0.1/(bin.port)/mega-chunked") - let response = try await client.execute(request, deadline: .now() + .seconds(10)) - - await XCTAssertThrowsError(try await response.body.collect(upTo: 100)) { error in - XCTAssert(error is NIOTooManyBytesError) - } - } - } - - func testAsyncSequenceReuse() { - XCTAsyncTest { - let bin = HTTPBin(.http2(compress: false)) { _ in HTTPEchoHandler() } - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - var request = HTTPClientRequest(url: "https://localhost/(bin.port)/") - request.method = .POST - request.body = .stream( - [ - ByteBuffer(string: "1"), - ByteBuffer(string: "2"), - ByteBuffer(string: "34"), - ].async, - length: .unknown - ) - - guard - let response1 = await XCTAssertNoThrowWithResult( - try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) - ) - else { return } - XCTAssertEqual(response1.headers["content-length"], []) - guard - let body = await XCTAssertNoThrowWithResult( - try await response1.body.collect(upTo: 1024) - ) - else { return } - XCTAssertEqual(body, ByteBuffer(string: "1234")) - - guard - let response2 = await XCTAssertNoThrowWithResult( - try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) - ) - else { return } - XCTAssertEqual(response2.headers["content-length"], []) - guard - let body = await XCTAssertNoThrowWithResult( - try await response2.body.collect(upTo: 1024) - ) - else { return } - XCTAssertEqual(body, ByteBuffer(string: "1234")) - } - } - - func testRejectsInvalidCharactersInHeaderFieldNames_http1() { - self._rejectsInvalidCharactersInHeaderFieldNames(mode: .http1_1(ssl: true)) - } - - func testRejectsInvalidCharactersInHeaderFieldNames_http2() { - self._rejectsInvalidCharactersInHeaderFieldNames(mode: .http2(compress: false)) - } - - private func _rejectsInvalidCharactersInHeaderFieldNames(mode: HTTPBin.Mode) { - XCTAsyncTest { - let bin = HTTPBin(mode) - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - - // The spec in [RFC 9110](https://httpwg.org/specs/rfc9110.html#fields.values) defines the valid - // characters as the following: - // - // ``` - // field-name = token - // - // token = 1*tchar - // - // tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" - // / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" - // / DIGIT / ALPHA - // ; any VCHAR, except delimiters - let weirdAllowedFieldName = "!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - - var request = HTTPClientRequest(url: "https://localhost/(bin.port)/get") - request.headers.add(name: weirdAllowedFieldName, value: "present") - - // This should work fine. - guard - let response = await XCTAssertNoThrowWithResult( - try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) - ) - else { - return - } - - XCTAssertEqual(response.status, .ok) - - // Now, let's confirm all other bytes are rejected. We want to stay within the ASCII space as the HTTPHeaders type will forbid anything else. - for byte in UInt8(0)...UInt8(127) { - // Skip bytes that we already believe are allowed. - if weirdAllowedFieldName.utf8.contains(byte) { - continue - } - let forbiddenFieldName = weirdAllowedFieldName + String(decoding: [byte], as: UTF8.self) - - var request = HTTPClientRequest(url: "https://localhost/(bin.port)/get") - request.headers.add(name: forbiddenFieldName, value: "present") - - await XCTAssertThrowsError( - try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) - ) { error in - XCTAssertEqual(error as? HTTPClientError, .invalidHeaderFieldNames([forbiddenFieldName])) - } - } - } - } - - func testRejectsInvalidCharactersInHeaderFieldValues_http1() { - self._rejectsInvalidCharactersInHeaderFieldValues(mode: .http1_1(ssl: true)) - } - - func testRejectsInvalidCharactersInHeaderFieldValues_http2() { - self._rejectsInvalidCharactersInHeaderFieldValues(mode: .http2(compress: false)) - } - - private func _rejectsInvalidCharactersInHeaderFieldValues(mode: HTTPBin.Mode) { - XCTAsyncTest { - let bin = HTTPBin(mode) - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - - // We reject all ASCII control characters except HTAB and tolerate everything else. - let weirdAllowedFieldValue = - "!\" \t#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" - - var request = HTTPClientRequest(url: "https://localhost/(bin.port)/get") - request.headers.add(name: "Weird-Value", value: weirdAllowedFieldValue) - - // This should work fine. - guard - let response = await XCTAssertNoThrowWithResult( - try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) - ) - else { - return - } - - XCTAssertEqual(response.status, .ok) - - // Now, let's confirm all other bytes in the ASCII range ar rejected - for byte in UInt8(0)...UInt8(127) { - // Skip bytes that we already believe are allowed. - if weirdAllowedFieldValue.utf8.contains(byte) { - continue - } - let forbiddenFieldValue = weirdAllowedFieldValue + String(decoding: [byte], as: UTF8.self) - - var request = HTTPClientRequest(url: "https://localhost/(bin.port)/get") - request.headers.add(name: "Weird-Value", value: forbiddenFieldValue) - - await XCTAssertThrowsError( - try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) - ) { error in - XCTAssertEqual(error as? HTTPClientError, .invalidHeaderFieldValues([forbiddenFieldValue])) - } - } - - // All the bytes outside the ASCII range are fine though. - for byte in UInt8(128)...UInt8(255) { - let evenWeirderAllowedValue = weirdAllowedFieldValue + String(decoding: [byte], as: UTF8.self) - - var request = HTTPClientRequest(url: "https://localhost/(bin.port)/get") - request.headers.add(name: "Weird-Value", value: evenWeirderAllowedValue) - - // This should work fine. - guard - let response = await XCTAssertNoThrowWithResult( - try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) - ) - else { - return - } - - XCTAssertEqual(response.status, .ok) - } - } - } - - func testUsingGetMethodInsteadOfWait() { - XCTAsyncTest { - let bin = HTTPBin(.http2(compress: false)) - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let request = try HTTPClient.Request(url: "https://localhost/(bin.port)/get") - - guard - let response = await XCTAssertNoThrowWithResult( - try await client.execute(request: request).get() - ) - else { - return - } - - XCTAssertEqual(response.status, .ok) - XCTAssertEqual(response.version, .http2) - } - } - - func testSimpleContentLengthErrorNoBody() { - XCTAsyncTest { - let bin = HTTPBin(.http2(compress: false)) - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - let request = HTTPClientRequest(url: "https://localhost/(bin.port)/content-length-without-body") - guard - let response = await XCTAssertNoThrowWithResult( - try await client.execute(request, deadline: .now() + .seconds(10), logger: logger) - ) - else { return } - await XCTAssertThrowsError( - try await response.body.collect(upTo: 3) - ) { - XCTAssertEqualTypeAndValue($0, NIOTooManyBytesError(maxBytes: 3)) - } - } - } -} - -struct AnySendableSequence: @unchecked Sendable { - private let wrapped: AnySequence - init( - _ sequence: WrappedSequence - ) where WrappedSequence.Element == Element { - self.wrapped = .init(sequence) - } -} - -extension AnySendableSequence: Sequence { - func makeIterator() -> AnySequence.Iterator { - self.wrapped.makeIterator() - } -} - -struct AnySendableCollection: @unchecked Sendable { - private let wrapped: AnyCollection - init( - _ collection: WrappedCollection - ) where WrappedCollection.Element == Element { - self.wrapped = .init(collection) - } -} - -extension AnySendableCollection: Collection { - var startIndex: AnyCollection.Index { - self.wrapped.startIndex - } - - var endIndex: AnyCollection.Index { - self.wrapped.endIndex - } - - func index(after i: AnyIndex) -> AnyIndex { - self.wrapped.index(after: i) - } - - subscript(position: AnyCollection.Index) -> Element { - self.wrapped[position] - } -} diff --git a/Tests/AsyncHTTPClientTests/AsyncTestHelpers.swift b/Tests/AsyncHTTPClientTests/AsyncTestHelpers.swift deleted file mode 100644 index 4a5c8d486..000000000 --- a/Tests/AsyncHTTPClientTests/AsyncTestHelpers.swift +++ /dev/null @@ -1,195 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOConcurrencyHelpers -import NIOCore - -/// ``AsyncSequenceWriter`` is `Sendable` because its state is protected by a Lock -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -final class AsyncSequenceWriter: AsyncSequence, @unchecked Sendable { - typealias AsyncIterator = Iterator - - struct Iterator: AsyncIteratorProtocol { - private let writer: AsyncSequenceWriter - - init(_ writer: AsyncSequenceWriter) { - self.writer = writer - } - - mutating func next() async throws -> Element? { - try await self.writer.next() - } - } - - func makeAsyncIterator() -> Iterator { - Iterator(self) - } - - private enum State { - case buffering(CircularBuffer, CheckedContinuation?) - case finished - case waiting(CheckedContinuation) - case failed(Error, CheckedContinuation?) - } - - private var _state = State.buffering(.init(), nil) - private let lock = NIOLock() - - public var hasDemand: Bool { - self.lock.withLock { - switch self._state { - case .failed, .finished, .buffering: - return false - case .waiting: - return true - } - } - } - - /// Wait until a downstream consumer has issued more demand by calling `next`. - public func demand() async { - self.lock.lock() - - switch self._state { - case .buffering(let buffer, .none): - await withCheckedContinuation { (continuation: CheckedContinuation) in - self._state = .buffering(buffer, continuation) - self.lock.unlock() - } - - case .waiting: - self.lock.unlock() - return - - case .buffering(_, .some), .failed(_, .some): - let state = self._state - self.lock.unlock() - preconditionFailure("Already waiting for demand. Invalid state: \(state)") - - case .finished, .failed: - let state = self._state - self.lock.unlock() - preconditionFailure("Invalid state: \(state)") - } - } - - private func next() async throws -> Element? { - self.lock.lock() - switch self._state { - case .buffering(let buffer, let demandContinuation) where buffer.isEmpty: - return try await withCheckedThrowingContinuation { continuation in - self._state = .waiting(continuation) - self.lock.unlock() - demandContinuation?.resume(returning: ()) - } - - case .buffering(var buffer, let demandContinuation): - let first = buffer.removeFirst() - if first != nil { - self._state = .buffering(buffer, demandContinuation) - } else { - self._state = .finished - } - self.lock.unlock() - return first - - case .failed(let error, let demandContinuation): - self._state = .finished - self.lock.unlock() - demandContinuation?.resume() - throw error - - case .finished: - self.lock.unlock() - return nil - - case .waiting: - let state = self._state - self.lock.unlock() - preconditionFailure( - "Expected that there is always only one concurrent call to next. Invalid state: \(state)" - ) - } - } - - public func write(_ element: Element) { - self.writeBufferOrEnd(element) - } - - public func end() { - self.writeBufferOrEnd(nil) - } - - private enum WriteAction { - case succeedContinuation(CheckedContinuation, Element?) - case none - } - - private func writeBufferOrEnd(_ element: Element?) { - let writeAction = self.lock.withLock { () -> WriteAction in - switch self._state { - case .buffering(var buffer, let continuation): - buffer.append(element) - self._state = .buffering(buffer, continuation) - return .none - - case .waiting(let continuation): - self._state = .buffering(.init(), nil) - return .succeedContinuation(continuation, element) - - case .finished, .failed: - preconditionFailure("Invalid state: \(self._state)") - } - } - - switch writeAction { - case .succeedContinuation(let continuation, let element): - continuation.resume(returning: element) - - case .none: - break - } - } - - private enum ErrorAction { - case failContinuation(CheckedContinuation, Error) - case none - } - - /// Drops all buffered writes and emits an error on the waiting `next`. If there is no call to `next` - /// waiting, will emit the error on the next call to `next`. - public func fail(_ error: Error) { - let errorAction = self.lock.withLock { () -> ErrorAction in - switch self._state { - case .buffering(_, let demandContinuation): - self._state = .failed(error, demandContinuation) - return .none - - case .failed, .finished: - return .none - - case .waiting(let continuation): - self._state = .finished - return .failContinuation(continuation, error) - } - } - - switch errorAction { - case .failContinuation(let checkedContinuation, let error): - checkedContinuation.resume(throwing: error) - case .none: - break - } - } -} diff --git a/Tests/AsyncHTTPClientTests/ConnectionPoolSizeConfigValueIsRespectedTests.swift b/Tests/AsyncHTTPClientTests/ConnectionPoolSizeConfigValueIsRespectedTests.swift deleted file mode 100644 index 962791334..000000000 --- a/Tests/AsyncHTTPClientTests/ConnectionPoolSizeConfigValueIsRespectedTests.swift +++ /dev/null @@ -1,76 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2022 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import AsyncHTTPClient -import Atomics -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOFoundationCompat -import NIOHTTP1 -import NIOHTTPCompression -import NIOPosix -import NIOSSL -import NIOTestUtils -import NIOTransportServices -import XCTest - -#if canImport(Network) -import Network -#endif - -final class ConnectionPoolSizeConfigValueIsRespectedTests: XCTestCaseHTTPClientTestsBaseClass { - func testConnectionPoolSizeConfigValueIsRespected() { - let numberOfRequestsPerThread = 1000 - let numberOfParallelWorkers = 16 - let poolSize = 12 - - let httpBin = HTTPBin() - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let group = MultiThreadedEventLoopGroup(numberOfThreads: 4) - defer { XCTAssertNoThrow(try group.syncShutdownGracefully()) } - - let configuration = HTTPClient.Configuration( - connectionPool: .init( - idleTimeout: .seconds(30), - concurrentHTTP1ConnectionsPerHostSoftLimit: poolSize - ) - ) - let client = HTTPClient(eventLoopGroupProvider: .shared(group), configuration: configuration) - defer { XCTAssertNoThrow(try client.syncShutdown()) } - - let g = DispatchGroup() - for workerID in 0.. Void = { _ in }) throws { - let part = try self.readOutbound(as: HTTPClientRequestPart.self) - switch part { - case .head(let head): - try verify(head) - case .body, .end: - throw HTTP1EmbeddedChannelError(reason: "Expected .head but got '\(part!)'") - case .none: - throw HTTP1EmbeddedChannelError(reason: "Nothing in buffer") - } - } - - public func receiveBodyAndVerify(_ verify: (IOData) throws -> Void = { _ in }) throws { - let part = try self.readOutbound(as: HTTPClientRequestPart.self) - switch part { - case .body(let iodata): - try verify(iodata) - case .head, .end: - throw HTTP1EmbeddedChannelError(reason: "Expected .head but got '\(part!)'") - case .none: - throw HTTP1EmbeddedChannelError(reason: "Nothing in buffer") - } - } - - public func receiveEnd() throws { - let part = try self.readOutbound(as: HTTPClientRequestPart.self) - switch part { - case .end: - break - case .head, .body: - throw HTTP1EmbeddedChannelError(reason: "Expected .head but got '\(part!)'") - case .none: - throw HTTP1EmbeddedChannelError(reason: "Nothing in buffer") - } - } -} - -struct HTTP1TestTools { - let connection: HTTP1Connection.SendableView - let connectionDelegate: MockConnectionDelegate - let readEventHandler: ReadEventHitHandler - let logger: Logger -} - -extension EmbeddedChannel { - func setupHTTP1Connection() throws -> HTTP1TestTools { - let logger = Logger(label: "test") - let readEventHandler = ReadEventHitHandler() - - try self.pipeline.syncOperations.addHandler(readEventHandler) - try self.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait() - - let connectionDelegate = MockConnectionDelegate() - let connection = try HTTP1Connection.start( - channel: self, - connectionID: 1, - delegate: connectionDelegate, - decompression: .disabled, - logger: logger - ) - - // remove HTTP client encoder and decoder - - let decoder = try self.pipeline.syncOperations.handler(type: ByteToMessageHandler.self) - let encoder = try self.pipeline.syncOperations.handler(type: HTTPRequestEncoder.self) - - let removeDecoderFuture = self.pipeline.syncOperations.removeHandler(decoder) - let removeEncoderFuture = self.pipeline.syncOperations.removeHandler(encoder) - - self.embeddedEventLoop.run() - - try removeDecoderFuture.wait() - try removeEncoderFuture.wait() - - return .init( - connection: connection.sendableView, - connectionDelegate: connectionDelegate, - readEventHandler: readEventHandler, - logger: logger - ) - } -} - -public struct HTTP1EmbeddedChannelError: Error, Hashable, CustomStringConvertible { - public var reason: String - - public init(reason: String) { - self.reason = reason - } - - public var description: String { - self.reason - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTP1ClientChannelHandlerTests.swift b/Tests/AsyncHTTPClientTests/HTTP1ClientChannelHandlerTests.swift deleted file mode 100644 index 0d871b7dc..000000000 --- a/Tests/AsyncHTTPClientTests/HTTP1ClientChannelHandlerTests.swift +++ /dev/null @@ -1,1120 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOEmbedded -import NIOHTTP1 -import XCTest - -@testable import AsyncHTTPClient - -class HTTP1ClientChannelHandlerTests: XCTestCase { - func testResponseBackpressure() { - let embedded = EmbeddedChannel() - var maybeTestUtils: HTTP1TestTools? - XCTAssertNoThrow(maybeTestUtils = try embedded.setupHTTP1Connection()) - guard let testUtils = maybeTestUtils else { return XCTFail("Expected connection setup works") } - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "http://localhost/")) - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseBackpressureDelegate(eventLoop: embedded.eventLoop) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: testUtils.logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - testUtils.connection.executeRequest(requestBag) - - XCTAssertNoThrow( - try embedded.receiveHeadAndVerify { - XCTAssertEqual($0.method, .GET) - XCTAssertEqual($0.uri, "/") - XCTAssertEqual($0.headers.first(name: "host"), "localhost") - } - ) - XCTAssertEqual(try embedded.readOutbound(as: HTTPClientRequestPart.self), .end(nil)) - - let responseHead = HTTPResponseHead( - version: .http1_1, - status: .ok, - headers: HTTPHeaders([("content-length", "12")]) - ) - - XCTAssertEqual(testUtils.readEventHandler.readHitCounter, 0) - embedded.read() - XCTAssertEqual(testUtils.readEventHandler.readHitCounter, 1) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.head(responseHead))) - - let part0 = ByteBuffer(bytes: 0...3) - let part1 = ByteBuffer(bytes: 4...7) - let part2 = ByteBuffer(bytes: 8...11) - - // part 0. Demand first, read second - XCTAssertEqual(testUtils.readEventHandler.readHitCounter, 1) - let part0Future = delegate.next() - XCTAssertEqual(testUtils.readEventHandler.readHitCounter, 1) - embedded.read() - XCTAssertEqual(testUtils.readEventHandler.readHitCounter, 2) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.body(part0))) - XCTAssertEqual(try part0Future.wait(), part0) - - // part 1. read first, demand second - - XCTAssertEqual(testUtils.readEventHandler.readHitCounter, 2) - embedded.read() - XCTAssertEqual(testUtils.readEventHandler.readHitCounter, 2) - let part1Future = delegate.next() - XCTAssertEqual(testUtils.readEventHandler.readHitCounter, 3) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.body(part1))) - XCTAssertEqual(try part1Future.wait(), part1) - - // part 2. Demand first, read second - XCTAssertEqual(testUtils.readEventHandler.readHitCounter, 3) - let part2Future = delegate.next() - XCTAssertEqual(testUtils.readEventHandler.readHitCounter, 3) - embedded.read() - XCTAssertEqual(testUtils.readEventHandler.readHitCounter, 4) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.body(part2))) - XCTAssertEqual(try part2Future.wait(), part2) - - // end. read first, demand second - XCTAssertEqual(testUtils.readEventHandler.readHitCounter, 4) - embedded.read() - XCTAssertEqual(testUtils.readEventHandler.readHitCounter, 4) - let endFuture = delegate.next() - XCTAssertEqual(testUtils.readEventHandler.readHitCounter, 5) - XCTAssertEqual(testUtils.connectionDelegate.hitConnectionReleased, 0) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.end(nil))) - XCTAssertEqual(testUtils.connectionDelegate.hitConnectionReleased, 1) - XCTAssertEqual(try endFuture.wait(), .none) - - XCTAssertNoThrow(try requestBag.task.futureResult.wait()) - } - - func testWriteBackpressure() { - let embedded = EmbeddedChannel() - let testWriter = TestBackpressureWriter(eventLoop: embedded.eventLoop, parts: 50) - var maybeTestUtils: HTTP1TestTools? - XCTAssertNoThrow(maybeTestUtils = try embedded.setupHTTP1Connection()) - guard let testUtils = maybeTestUtils else { return XCTFail("Expected connection setup works") } - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow( - maybeRequest = try HTTPClient.Request( - url: "http://localhost/", - method: .POST, - body: .stream(contentLength: 100) { writer in - testWriter.start(writer: writer) - } - ) - ) - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseAccumulator(request: request) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: testUtils.logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(idleReadTimeout: .milliseconds(200)), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - // the handler only writes once the channel is writable - embedded.isWritable = false - testWriter.writabilityChanged(false) - embedded.pipeline.fireChannelWritabilityChanged() - testUtils.connection.executeRequest(requestBag) - - XCTAssertEqual(try embedded.readOutbound(as: HTTPClientRequestPart.self), .none) - - embedded.isWritable = true - testWriter.writabilityChanged(true) - embedded.pipeline.fireChannelWritabilityChanged() - - XCTAssertNoThrow( - try embedded.receiveHeadAndVerify { - XCTAssertEqual($0.method, .POST) - XCTAssertEqual($0.uri, "/") - XCTAssertEqual($0.headers.first(name: "host"), "localhost") - XCTAssertEqual($0.headers.first(name: "content-length"), "100") - } - ) - - // the next body write will be executed once we tick the el. before we make the channel - // unwritable - - for index in 0..<50 { - embedded.isWritable = false - testWriter.writabilityChanged(false) - embedded.pipeline.fireChannelWritabilityChanged() - - XCTAssertEqual(testWriter.written, index) - - embedded.embeddedEventLoop.run() - - XCTAssertNoThrow( - try embedded.receiveBodyAndVerify { - XCTAssertEqual($0.readableBytes, 2) - } - ) - - XCTAssertEqual(testWriter.written, index + 1) - - embedded.isWritable = true - testWriter.writabilityChanged(true) - embedded.pipeline.fireChannelWritabilityChanged() - } - - embedded.embeddedEventLoop.run() - XCTAssertNoThrow(try embedded.receiveEnd()) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.head(responseHead))) - embedded.read() - - XCTAssertEqual(testUtils.connectionDelegate.hitConnectionClosed, 0) - XCTAssertEqual(testUtils.connectionDelegate.hitConnectionReleased, 0) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.end(nil))) - XCTAssertEqual(testUtils.connectionDelegate.hitConnectionClosed, 0) - XCTAssertEqual(testUtils.connectionDelegate.hitConnectionReleased, 1) - - XCTAssertNoThrow(try requestBag.task.futureResult.wait()) - } - - func testClientHandlerCancelsRequestIfWeWantToShutdown() { - let embedded = EmbeddedChannel() - var maybeTestUtils: HTTP1TestTools? - XCTAssertNoThrow(maybeTestUtils = try embedded.setupHTTP1Connection()) - guard let testUtils = maybeTestUtils else { return XCTFail("Expected connection setup works") } - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "http://localhost/")) - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseAccumulator(request: request) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: testUtils.logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(idleReadTimeout: .milliseconds(200)), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - testUtils.connection.executeRequest(requestBag) - - XCTAssertNoThrow( - try embedded.receiveHeadAndVerify { - XCTAssertEqual($0.method, .GET) - XCTAssertEqual($0.uri, "/") - XCTAssertEqual($0.headers.first(name: "host"), "localhost") - } - ) - XCTAssertNoThrow(try embedded.receiveEnd()) - - XCTAssertTrue(embedded.isActive) - XCTAssertEqual(testUtils.connectionDelegate.hitConnectionClosed, 0) - XCTAssertEqual(testUtils.connectionDelegate.hitConnectionReleased, 0) - testUtils.connection.shutdown() - XCTAssertFalse(embedded.isActive) - embedded.embeddedEventLoop.run() - XCTAssertEqual(testUtils.connectionDelegate.hitConnectionClosed, 1) - XCTAssertEqual(testUtils.connectionDelegate.hitConnectionReleased, 0) - - XCTAssertThrowsError(try requestBag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .cancelled) - } - } - - func testIdleReadTimeout() { - let embedded = EmbeddedChannel() - var maybeTestUtils: HTTP1TestTools? - XCTAssertNoThrow(maybeTestUtils = try embedded.setupHTTP1Connection()) - guard let testUtils = maybeTestUtils else { return XCTFail("Expected connection setup works") } - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "http://localhost/")) - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseBackpressureDelegate(eventLoop: embedded.eventLoop) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: testUtils.logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(idleReadTimeout: .milliseconds(200)), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - testUtils.connection.executeRequest(requestBag) - - XCTAssertNoThrow( - try embedded.receiveHeadAndVerify { - XCTAssertEqual($0.method, .GET) - XCTAssertEqual($0.uri, "/") - XCTAssertEqual($0.headers.first(name: "host"), "localhost") - } - ) - XCTAssertNoThrow(try embedded.receiveEnd()) - - let responseHead = HTTPResponseHead( - version: .http1_1, - status: .ok, - headers: HTTPHeaders([("content-length", "12")]) - ) - - XCTAssertEqual(testUtils.readEventHandler.readHitCounter, 0) - embedded.read() - XCTAssertEqual(testUtils.readEventHandler.readHitCounter, 1) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.head(responseHead))) - - // not sending anything after the head should lead to request fail and connection close - - XCTAssertEqual(testUtils.connectionDelegate.hitConnectionClosed, 0) - XCTAssertEqual(testUtils.connectionDelegate.hitConnectionReleased, 0) - embedded.embeddedEventLoop.advanceTime(by: .milliseconds(250)) - XCTAssertEqual(testUtils.connectionDelegate.hitConnectionClosed, 1) - XCTAssertEqual(testUtils.connectionDelegate.hitConnectionReleased, 0) - - XCTAssertThrowsError(try requestBag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .readTimeout) - } - } - - func testIdleReadTimeoutIsCanceledIfRequestIsCanceled() { - let embedded = EmbeddedChannel() - var maybeTestUtils: HTTP1TestTools? - XCTAssertNoThrow(maybeTestUtils = try embedded.setupHTTP1Connection()) - guard let testUtils = maybeTestUtils else { return XCTFail("Expected connection setup works") } - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "http://localhost/")) - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseBackpressureDelegate(eventLoop: embedded.eventLoop) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: testUtils.logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(idleReadTimeout: .milliseconds(200)), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - testUtils.connection.executeRequest(requestBag) - - XCTAssertNoThrow( - try embedded.receiveHeadAndVerify { - XCTAssertEqual($0.method, .GET) - XCTAssertEqual($0.uri, "/") - XCTAssertEqual($0.headers.first(name: "host"), "localhost") - } - ) - XCTAssertNoThrow(try embedded.receiveEnd()) - - let responseHead = HTTPResponseHead( - version: .http1_1, - status: .ok, - headers: HTTPHeaders([("content-length", "12")]) - ) - - XCTAssertEqual(testUtils.readEventHandler.readHitCounter, 0) - embedded.read() - XCTAssertEqual(testUtils.readEventHandler.readHitCounter, 1) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.head(responseHead))) - - // canceling the request - requestBag.fail(HTTPClientError.cancelled) - XCTAssertThrowsError(try requestBag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .cancelled) - } - - // the idle read timeout should be cleared because we canceled the request - // therefore advancing the time should not trigger a crash - embedded.embeddedEventLoop.advanceTime(by: .milliseconds(250)) - } - - func testIdleWriteTimeout() { - let embedded = EmbeddedChannel() - let testWriter = TestBackpressureWriter(eventLoop: embedded.eventLoop, parts: 5) - var maybeTestUtils: HTTP1TestTools? - XCTAssertNoThrow(maybeTestUtils = try embedded.setupHTTP1Connection()) - guard let testUtils = maybeTestUtils else { return XCTFail("Expected connection setup works") } - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow( - maybeRequest = try HTTPClient.Request( - url: "http://localhost/", - method: .POST, - body: .stream(contentLength: 10) { writer in - // Advance time by more than the idle write timeout (that's 1 millisecond) to trigger the timeout. - embedded.embeddedEventLoop.advanceTime(by: .milliseconds(2)) - return testWriter.start(writer: writer) - } - ) - ) - - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseAccumulator(request: request) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: testUtils.logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(idleWriteTimeout: .milliseconds(1)), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - embedded.isWritable = true - testWriter.writabilityChanged(true) - embedded.pipeline.fireChannelWritabilityChanged() - testUtils.connection.executeRequest(requestBag) - - XCTAssertThrowsError(try requestBag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .writeTimeout) - } - } - - func testIdleWriteTimeoutRaceToEnd() { - let embedded = EmbeddedChannel() - var maybeTestUtils: HTTP1TestTools? - XCTAssertNoThrow(maybeTestUtils = try embedded.setupHTTP1Connection()) - guard let testUtils = maybeTestUtils else { return XCTFail("Expected connection setup works") } - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow( - maybeRequest = try HTTPClient.Request( - url: "http://localhost/", - method: .POST, - body: .stream { _ in - // Advance time by more than the idle write timeout (that's 1 millisecond) to trigger the timeout. - let scheduled = embedded.embeddedEventLoop.flatScheduleTask(in: .milliseconds(2)) { - embedded.embeddedEventLoop.makeSucceededVoidFuture() - } - return scheduled.futureResult - } - ) - ) - - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseAccumulator(request: request) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: testUtils.logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(idleWriteTimeout: .milliseconds(5)), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - embedded.isWritable = true - embedded.pipeline.fireChannelWritabilityChanged() - testUtils.connection.executeRequest(requestBag) - let expectedHeaders: HTTPHeaders = ["host": "localhost", "Transfer-Encoding": "chunked"] - XCTAssertEqual( - try embedded.readOutbound(as: HTTPClientRequestPart.self), - .head(HTTPRequestHead(version: .http1_1, method: .POST, uri: "/", headers: expectedHeaders)) - ) - - // change the writability to false. - embedded.isWritable = false - embedded.pipeline.fireChannelWritabilityChanged() - embedded.embeddedEventLoop.run() - - // let the writer, write an end (while writability is false) - embedded.embeddedEventLoop.advanceTime(by: .milliseconds(2)) - - XCTAssertEqual(try embedded.readOutbound(as: HTTPClientRequestPart.self), .end(nil)) - } - - func testIdleWriteTimeoutWritabilityChanged() { - let embedded = EmbeddedChannel() - let testWriter = TestBackpressureWriter(eventLoop: embedded.eventLoop, parts: 5) - var maybeTestUtils: HTTP1TestTools? - XCTAssertNoThrow(maybeTestUtils = try embedded.setupHTTP1Connection()) - guard let testUtils = maybeTestUtils else { return XCTFail("Expected connection setup works") } - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow( - maybeRequest = try HTTPClient.Request( - url: "http://localhost/", - method: .POST, - body: .stream(contentLength: 10) { writer in - embedded.isWritable = false - embedded.pipeline.fireChannelWritabilityChanged() - // This should not trigger any errors or timeouts, because the timer isn't running - // as the channel is not writable. - embedded.embeddedEventLoop.advanceTime(by: .milliseconds(20)) - - // Now that the channel will become writable, this should trigger a timeout. - embedded.isWritable = true - embedded.pipeline.fireChannelWritabilityChanged() - embedded.embeddedEventLoop.advanceTime(by: .milliseconds(2)) - - return testWriter.start(writer: writer) - } - ) - ) - - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseAccumulator(request: request) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: testUtils.logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(idleWriteTimeout: .milliseconds(1)), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - embedded.isWritable = true - testWriter.writabilityChanged(true) - embedded.pipeline.fireChannelWritabilityChanged() - testUtils.connection.executeRequest(requestBag) - - XCTAssertThrowsError(try requestBag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .writeTimeout) - } - } - - func testIdleWriteTimeoutIsCancelledIfRequestIsCancelled() { - let embedded = EmbeddedChannel() - let testWriter = TestBackpressureWriter(eventLoop: embedded.eventLoop, parts: 1) - var maybeTestUtils: HTTP1TestTools? - XCTAssertNoThrow(maybeTestUtils = try embedded.setupHTTP1Connection()) - guard let testUtils = maybeTestUtils else { return XCTFail("Expected connection setup works") } - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow( - maybeRequest = try HTTPClient.Request( - url: "http://localhost/", - method: .POST, - body: .stream(contentLength: 2) { writer in - testWriter.start(writer: writer, expectedErrors: [HTTPClientError.cancelled]) - } - ) - ) - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseAccumulator(request: request) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: testUtils.logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(idleWriteTimeout: .milliseconds(1)), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - embedded.isWritable = true - testWriter.writabilityChanged(true) - embedded.pipeline.fireChannelWritabilityChanged() - testUtils.connection.executeRequest(requestBag) - - // canceling the request - requestBag.fail(HTTPClientError.cancelled) - XCTAssertThrowsError(try requestBag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .cancelled) - } - - // the idle write timeout should be cleared because we canceled the request - // therefore advancing the time should not trigger a crash - embedded.embeddedEventLoop.advanceTime(by: .milliseconds(250)) - } - - func testFailHTTPRequestWithContentLengthBecauseOfChannelInactiveWaitingForDemand() { - let embedded = EmbeddedChannel() - var maybeTestUtils: HTTP1TestTools? - XCTAssertNoThrow(maybeTestUtils = try embedded.setupHTTP1Connection()) - guard let testUtils = maybeTestUtils else { return XCTFail("Expected connection setup works") } - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "http://localhost/")) - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseBackpressureDelegate(eventLoop: embedded.eventLoop) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: testUtils.logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - testUtils.connection.executeRequest(requestBag) - - XCTAssertNoThrow( - try embedded.receiveHeadAndVerify { - XCTAssertEqual($0.method, .GET) - XCTAssertEqual($0.uri, "/") - XCTAssertEqual($0.headers.first(name: "host"), "localhost") - } - ) - XCTAssertNoThrow(try embedded.receiveEnd()) - - let responseHead = HTTPResponseHead( - version: .http1_1, - status: .ok, - headers: HTTPHeaders([("content-length", "50")]) - ) - - XCTAssertEqual(testUtils.readEventHandler.readHitCounter, 0) - embedded.read() - XCTAssertEqual(testUtils.readEventHandler.readHitCounter, 1) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.head(responseHead))) - - // not sending anything after the head should lead to request fail and connection close - embedded.pipeline.fireChannelReadComplete() - embedded.pipeline.read() - XCTAssertEqual(testUtils.readEventHandler.readHitCounter, 2) - - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.body(ByteBuffer(string: "foo bar")))) - embedded.pipeline.fireChannelReadComplete() - // We miss a `embedded.pipeline.read()` here by purpose. - XCTAssertEqual(testUtils.readEventHandler.readHitCounter, 2) - - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.body(ByteBuffer(string: "last bytes")))) - embedded.pipeline.fireChannelReadComplete() - embedded.pipeline.fireChannelInactive() - - XCTAssertThrowsError(try requestBag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .remoteConnectionClosed) - } - } - - func testWriteHTTPHeadFails() { - struct WriteError: Error, Equatable {} - - class FailWriteHandler: ChannelOutboundHandler { - typealias OutboundIn = HTTPClientRequestPart - typealias OutboundOut = HTTPClientRequestPart - - func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise?) { - let error = WriteError() - promise?.fail(error) - context.fireErrorCaught(error) - } - } - - let bodies: [HTTPClient.Body?] = [ - .none, - .some(.byteBuffer(ByteBuffer(string: "hello world"))), - ] - - for body in bodies { - let embedded = EmbeddedChannel() - var maybeTestUtils: HTTP1TestTools? - XCTAssertNoThrow(maybeTestUtils = try embedded.setupHTTP1Connection()) - guard let testUtils = maybeTestUtils else { return XCTFail("Expected connection setup works") } - - XCTAssertNoThrow( - try embedded.pipeline.syncOperations.addHandler( - FailWriteHandler(), - position: .after(testUtils.readEventHandler) - ) - ) - - let logger = Logger(label: "test") - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "http://localhost/", method: .POST, body: body)) - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseAccumulator(request: request) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(idleReadTimeout: .milliseconds(200)), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { - return XCTFail("Expected to be able to create a request bag") - } - - embedded.isWritable = false - XCTAssertNoThrow(try embedded.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait()) - embedded.write(requestBag, promise: nil) - - // the handler only writes once the channel is writable - XCTAssertEqual(try embedded.readOutbound(as: HTTPClientRequestPart.self), .none) - embedded.isWritable = true - embedded.pipeline.fireChannelWritabilityChanged() - - XCTAssertThrowsError(try requestBag.task.futureResult.wait()) { - XCTAssertEqual($0 as? WriteError, WriteError()) - } - - XCTAssertEqual(embedded.isActive, false) - } - } - - func testHandlerClosesChannelIfLastActionIsSendEndAndItFails() { - let embedded = EmbeddedChannel() - let testWriter = TestBackpressureWriter(eventLoop: embedded.eventLoop, parts: 5) - var maybeTestUtils: HTTP1TestTools? - XCTAssertNoThrow(maybeTestUtils = try embedded.setupHTTP1Connection()) - guard let testUtils = maybeTestUtils else { return XCTFail("Expected connection setup works") } - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow( - maybeRequest = try HTTPClient.Request( - url: "http://localhost/", - method: .POST, - body: .stream(contentLength: 10) { writer in - testWriter.start(writer: writer) - } - ) - ) - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseAccumulator(request: request) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: testUtils.logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(idleReadTimeout: .milliseconds(200)), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - XCTAssertNoThrow(try embedded.pipeline.addHandler(FailEndHandler(), position: .first).wait()) - - // Execute the request and we'll receive the head. - testWriter.writabilityChanged(true) - testUtils.connection.executeRequest(requestBag) - XCTAssertNoThrow( - try embedded.receiveHeadAndVerify { - XCTAssertEqual($0.method, .POST) - XCTAssertEqual($0.uri, "/") - XCTAssertEqual($0.headers.first(name: "host"), "localhost") - XCTAssertEqual($0.headers.first(name: "content-length"), "10") - } - ) - // We're going to immediately send the response head and end. - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.head(responseHead))) - embedded.read() - - // Send the end and confirm the connection is still live. - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.end(nil))) - XCTAssertEqual(testUtils.connectionDelegate.hitConnectionClosed, 0) - XCTAssertEqual(testUtils.connectionDelegate.hitConnectionReleased, 0) - - // Ok, now we can process some reads. We expect 5 reads, but we do _not_ expect an .end, because - // the `FailEndHandler` is going to fail it. - embedded.embeddedEventLoop.run() - XCTAssertEqual(testWriter.written, 5) - for _ in 0..<5 { - XCTAssertNoThrow( - try embedded.receiveBodyAndVerify { - XCTAssertEqual($0.readableBytes, 2) - } - ) - } - - embedded.embeddedEventLoop.run() - XCTAssertNil(try embedded.readOutbound(as: HTTPClientRequestPart.self)) - - // We should have seen the connection close, and the request is complete. - XCTAssertEqual(testUtils.connectionDelegate.hitConnectionClosed, 1) - XCTAssertEqual(testUtils.connectionDelegate.hitConnectionReleased, 0) - - XCTAssertThrowsError(try requestBag.task.futureResult.wait()) { error in - XCTAssertTrue(error is FailEndHandler.Error) - } - } - - func testChannelBecomesNonWritableDuringHeaderWrite() throws { - final class ChangeWritabilityOnFlush: ChannelOutboundHandler { - typealias OutboundIn = Any - func flush(context: ChannelHandlerContext) { - context.flush() - (context.channel as! EmbeddedChannel).isWritable = false - context.fireChannelWritabilityChanged() - } - } - let eventLoopGroup = EmbeddedEventLoopGroup(loops: 1) - let eventLoop = eventLoopGroup.next() as! EmbeddedEventLoop - let handler = HTTP1ClientChannelHandler( - eventLoop: eventLoop, - backgroundLogger: Logger(label: "no-op", factory: SwiftLogNoOpLogHandler.init), - connectionIdLoggerMetadata: "test connection" - ) - let channel = EmbeddedChannel( - handlers: [ - ChangeWritabilityOnFlush(), - handler, - ], - loop: eventLoop - ) - try channel.connect(to: .init(ipAddress: "127.0.0.1", port: 80)).wait() - - // non empty body is important to trigger this bug as we otherwise finish the request in a single flush - let request = MockHTTPExecutableRequest( - framingMetadata: RequestFramingMetadata(connectionClose: false, body: .fixedSize(1)), - raiseErrorIfUnimplementedMethodIsCalled: false - ) - channel.writeAndFlush(request, promise: nil) - XCTAssertEqual(request.events.map(\.kind), [.willExecuteRequest, .requestHeadSent]) - } - - func testIdleWriteTimeoutOutsideOfRunningState() { - let embedded = EmbeddedChannel() - var maybeTestUtils: HTTP1TestTools? - XCTAssertNoThrow(maybeTestUtils = try embedded.setupHTTP1Connection()) - print("pipeline", embedded.pipeline) - guard let testUtils = maybeTestUtils else { return XCTFail("Expected connection setup works") } - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "http://localhost/")) - guard var request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - // start a request stream we'll never write to - let streamPromise = embedded.eventLoop.makePromise(of: Void.self) - let streamCallback = { @Sendable (streamWriter: HTTPClient.Body.StreamWriter) -> EventLoopFuture in - streamPromise.futureResult - } - request.body = .init(contentLength: nil, stream: streamCallback) - - let accumulator = ResponseAccumulator(request: request) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: testUtils.logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests( - idleReadTimeout: .milliseconds(10), - idleWriteTimeout: .milliseconds(2) - ), - delegate: accumulator - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - testUtils.connection.executeRequest(requestBag) - - XCTAssertNoThrow( - try embedded.receiveHeadAndVerify { - XCTAssertEqual($0.method, .GET) - XCTAssertEqual($0.uri, "/") - XCTAssertEqual($0.headers.first(name: "host"), "localhost") - } - ) - - // close the pipeline to simulate a server-side close - // note this happens before we write so the idle write timeout is still running - try! embedded.pipeline.close().wait() - - // advance time to trigger the idle write timeout - // and ensure that the state machine can tolerate this - embedded.embeddedEventLoop.advanceTime(by: .milliseconds(250)) - } -} - -final class TestBackpressureWriter: Sendable { - let eventLoop: EventLoop - - let parts: Int - - var finishFuture: EventLoopFuture { self.finishPromise.futureResult } - private let finishPromise: EventLoopPromise - - private struct State { - var written = 0 - var channelIsWritable = false - } - - var written: Int { - self.state.value.written - } - - private let state: NIOLoopBoundBox - - init(eventLoop: EventLoop, parts: Int) { - self.eventLoop = eventLoop - self.parts = parts - self.state = .makeBoxSendingValue(State(), eventLoop: eventLoop) - self.finishPromise = eventLoop.makePromise(of: Void.self) - } - - func start(writer: HTTPClient.Body.StreamWriter, expectedErrors: [HTTPClientError] = []) -> EventLoopFuture { - @Sendable - func recursive() { - XCTAssert(self.eventLoop.inEventLoop) - XCTAssert(self.state.value.channelIsWritable) - if self.state.value.written == self.parts { - self.finishPromise.succeed(()) - } else { - self.eventLoop.execute { - let future = writer.write(.byteBuffer(.init(bytes: [0, 1]))) - self.state.value.written += 1 - future.whenComplete { result in - switch result { - case .success: - recursive() - case .failure(let error): - let isExpectedError = expectedErrors.contains { httpError in - if let castError = error as? HTTPClientError { - return castError == httpError - } - return false - } - if !isExpectedError { - XCTFail("Unexpected error: \(error)") - } - } - } - } - } - } - - recursive() - - return self.finishFuture - } - - func writabilityChanged(_ newValue: Bool) { - self.state.value.channelIsWritable = newValue - } -} - -final class ResponseBackpressureDelegate: HTTPClientResponseDelegate { - typealias Response = Void - - enum State: Sendable { - case consuming(EventLoopPromise) - case waitingForRemote(CircularBuffer>) - case buffering((ByteBuffer?, EventLoopPromise)?) - case done - } - - let eventLoop: EventLoop - private let state: NIOLoopBoundBox - - init(eventLoop: EventLoop) { - self.eventLoop = eventLoop - self.state = .makeBoxSendingValue(.consuming(eventLoop.makePromise(of: Void.self)), eventLoop: eventLoop) - } - - func next() -> EventLoopFuture { - switch self.state.value { - case .consuming(let backpressurePromise): - var promiseBuffer = CircularBuffer>() - let newPromise = self.eventLoop.makePromise(of: ByteBuffer?.self) - promiseBuffer.append(newPromise) - self.state.value = .waitingForRemote(promiseBuffer) - backpressurePromise.succeed(()) - return newPromise.futureResult - - case .waitingForRemote(var promiseBuffer): - assert( - !promiseBuffer.isEmpty, - "assert expected to be waiting if we have at least one promise in the buffer" - ) - let promise = self.eventLoop.makePromise(of: ByteBuffer?.self) - promiseBuffer.append(promise) - self.state.value = .waitingForRemote(promiseBuffer) - return promise.futureResult - - case .buffering(.none): - var promiseBuffer = CircularBuffer>() - let promise = self.eventLoop.makePromise(of: ByteBuffer?.self) - promiseBuffer.append(promise) - self.state.value = .waitingForRemote(promiseBuffer) - return promise.futureResult - - case .buffering(.some((let buffer, let promise))): - self.state.value = .buffering(nil) - promise.succeed(()) - return self.eventLoop.makeSucceededFuture(buffer) - - case .done: - return self.eventLoop.makeSucceededFuture(.none) - } - } - - func didReceiveHead(task: HTTPClient.Task, _ head: HTTPResponseHead) -> EventLoopFuture { - switch self.state.value { - case .consuming(let backpressurePromise): - return backpressurePromise.futureResult - - case .waitingForRemote: - return self.eventLoop.makeSucceededVoidFuture() - - case .buffering, .done: - preconditionFailure("State must be either waitingForRemote or initialized") - } - } - - func didReceiveBodyPart(task: HTTPClient.Task, _ buffer: ByteBuffer) -> EventLoopFuture { - switch self.state.value { - case .waitingForRemote(var promiseBuffer): - assert( - !promiseBuffer.isEmpty, - "assert expected to be waiting if we have at least one promise in the buffer" - ) - let promise = promiseBuffer.removeFirst() - if promiseBuffer.isEmpty { - let newBackpressurePromise = self.eventLoop.makePromise(of: Void.self) - self.state.value = .consuming(newBackpressurePromise) - promise.succeed(buffer) - return newBackpressurePromise.futureResult - } else { - self.state.value = .waitingForRemote(promiseBuffer) - promise.succeed(buffer) - return self.eventLoop.makeSucceededVoidFuture() - } - - case .buffering(.none): - let promise = self.eventLoop.makePromise(of: Void.self) - self.state.value = .buffering((buffer, promise)) - return promise.futureResult - - case .buffering(.some): - preconditionFailure( - "Did receive response part should not be called, before the previous promise was succeeded." - ) - - case .done, .consuming: - preconditionFailure("Invalid state: \(self.state)") - } - } - - func didFinishRequest(task: HTTPClient.Task) throws { - switch self.state.value { - case .waitingForRemote(let promiseBuffer): - for promise in promiseBuffer { - promise.succeed(.none) - } - self.state.value = .done - - case .buffering(.none): - self.state.value = .done - - case .done, .consuming: - preconditionFailure("Invalid state: \(self.state)") - - case .buffering(.some): - preconditionFailure( - "Did receive response part should not be called, before the previous promise was succeeded." - ) - } - } -} - -class ReadEventHitHandler: ChannelOutboundHandler { - public typealias OutboundIn = NIOAny - - private(set) var readHitCounter = 0 - - public init() {} - - public func read(context: ChannelHandlerContext) { - self.readHitCounter += 1 - context.read() - } -} - -final class FailEndHandler: ChannelOutboundHandler, Sendable { - typealias OutboundIn = HTTPClientRequestPart - typealias OutboundOut = HTTPClientRequestPart - - struct Error: Swift.Error {} - - func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise?) { - if case .end = self.unwrapOutboundIn(data) { - // We fail this. - promise?.fail(Self.Error()) - } else { - context.write(data, promise: promise) - } - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTP1ConnectionStateMachineTests.swift b/Tests/AsyncHTTPClientTests/HTTP1ConnectionStateMachineTests.swift deleted file mode 100644 index 1c6e9659f..000000000 --- a/Tests/AsyncHTTPClientTests/HTTP1ConnectionStateMachineTests.swift +++ /dev/null @@ -1,508 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOHTTP1 -import NIOHTTPCompression -import XCTest - -@testable import AsyncHTTPClient - -class HTTP1ConnectionStateMachineTests: XCTestCase { - func testPOSTRequestWithWriteAndReadBackpressure() { - var state = HTTP1ConnectionStateMachine() - XCTAssertEqual(state.channelActive(isWritable: false), .fireChannelActive) - - let requestHead = HTTPRequestHead(version: .http1_1, method: .POST, uri: "/", headers: ["content-length": "4"]) - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(4)) - XCTAssertEqual(state.runNewRequest(head: requestHead, metadata: metadata), .wait) - XCTAssertEqual(state.writabilityChanged(writable: true), .sendRequestHead(requestHead, sendEnd: false)) - XCTAssertEqual( - state.headSent(), - .notifyRequestHeadSendSuccessfully(resumeRequestBodyStream: true, startIdleTimer: false) - ) - - let part0 = IOData.byteBuffer(ByteBuffer(bytes: [0])) - let part1 = IOData.byteBuffer(ByteBuffer(bytes: [1])) - let part2 = IOData.byteBuffer(ByteBuffer(bytes: [2])) - let part3 = IOData.byteBuffer(ByteBuffer(bytes: [3])) - XCTAssertEqual(state.requestStreamPartReceived(part0, promise: nil), .sendBodyPart(part0, nil)) - XCTAssertEqual(state.requestStreamPartReceived(part1, promise: nil), .sendBodyPart(part1, nil)) - - // oh the channel reports... we should slow down producing... - XCTAssertEqual(state.writabilityChanged(writable: false), .pauseRequestBodyStream) - - // but we issued a .produceMoreRequestBodyData before... Thus, we must accept more produced - // data - XCTAssertEqual(state.requestStreamPartReceived(part2, promise: nil), .sendBodyPart(part2, nil)) - // however when we have put the data on the channel, we should not issue further - // .produceMoreRequestBodyData events - - // once we receive a writable event again, we can allow the producer to produce more data - XCTAssertEqual(state.writabilityChanged(writable: true), .resumeRequestBodyStream) - XCTAssertEqual(state.requestStreamPartReceived(part3, promise: nil), .sendBodyPart(part3, nil)) - XCTAssertEqual(state.requestStreamFinished(promise: nil), .sendRequestEnd(nil)) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - let responseBody = ByteBuffer(bytes: [1, 2, 3, 4]) - XCTAssertEqual(state.channelRead(.body(responseBody)), .wait) - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.informConnectionIsIdle, .init([responseBody]))) - XCTAssertEqual(state.channelReadComplete(), .wait) - } - - func testResponseReadingWithBackpressure() { - var state = HTTP1ConnectionStateMachine() - XCTAssertEqual(state.channelActive(isWritable: true), .fireChannelActive) - - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - let newRequestAction = state.runNewRequest(head: requestHead, metadata: metadata) - XCTAssertEqual(newRequestAction, .sendRequestHead(requestHead, sendEnd: true)) - XCTAssertEqual( - state.headSent(), - .notifyRequestHeadSendSuccessfully(resumeRequestBodyStream: false, startIdleTimer: true) - ) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok, headers: ["content-length": "12"]) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - let part0 = ByteBuffer(bytes: 0...3) - let part1 = ByteBuffer(bytes: 4...7) - let part2 = ByteBuffer(bytes: 8...11) - XCTAssertEqual(state.channelRead(.body(part0)), .wait) - XCTAssertEqual(state.channelRead(.body(part1)), .wait) - XCTAssertEqual(state.channelReadComplete(), .forwardResponseBodyParts(.init([part0, part1]))) - XCTAssertEqual(state.read(), .wait) - XCTAssertEqual(state.read(), .wait, "Expected to be able to consume a second read event") - XCTAssertEqual(state.demandMoreResponseBodyParts(), .read) - XCTAssertEqual(state.channelRead(.body(part2)), .wait) - XCTAssertEqual(state.channelReadComplete(), .forwardResponseBodyParts(.init([part2]))) - XCTAssertEqual(state.demandMoreResponseBodyParts(), .wait) - XCTAssertEqual(state.read(), .read) - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.informConnectionIsIdle, .init())) - XCTAssertEqual(state.channelReadComplete(), .wait) - XCTAssertEqual(state.read(), .read) - } - - func testWriteTimeoutAfterErrorDoesntCrash() { - var state = HTTP1ConnectionStateMachine() - XCTAssertEqual(state.channelActive(isWritable: true), .fireChannelActive) - - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - let newRequestAction = state.runNewRequest(head: requestHead, metadata: metadata) - XCTAssertEqual(newRequestAction, .sendRequestHead(requestHead, sendEnd: true)) - XCTAssertEqual( - state.headSent(), - .notifyRequestHeadSendSuccessfully(resumeRequestBodyStream: false, startIdleTimer: true) - ) - - struct MyError: Error, Equatable {} - XCTAssertEqual(state.errorHappened(MyError()), .failRequest(MyError(), .close(nil))) - - // Primarily we care that we don't crash here - XCTAssertEqual(state.idleWriteTimeoutTriggered(), .wait) - } - - func testAConnectionCloseHeaderInTheRequestLeadsToConnectionCloseAfterRequest() { - var state = HTTP1ConnectionStateMachine() - XCTAssertEqual(state.channelActive(isWritable: true), .fireChannelActive) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/", headers: ["connection": "close"]) - let metadata = RequestFramingMetadata(connectionClose: true, body: .fixedSize(0)) - let newRequestAction = state.runNewRequest(head: requestHead, metadata: metadata) - XCTAssertEqual(newRequestAction, .sendRequestHead(requestHead, sendEnd: true)) - XCTAssertEqual( - state.headSent(), - .notifyRequestHeadSendSuccessfully(resumeRequestBodyStream: false, startIdleTimer: true) - ) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - let responseBody = ByteBuffer(bytes: [1, 2, 3, 4]) - XCTAssertEqual(state.channelRead(.body(responseBody)), .wait) - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.close, .init([responseBody]))) - XCTAssertEqual(state.channelInactive(), .fireChannelInactive) - } - - func testAHTTP1_0ResponseWithoutKeepAliveHeaderLeadsToConnectionCloseAfterRequest() { - var state = HTTP1ConnectionStateMachine() - XCTAssertEqual(state.channelActive(isWritable: true), .fireChannelActive) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - let newRequestAction = state.runNewRequest(head: requestHead, metadata: metadata) - XCTAssertEqual(newRequestAction, .sendRequestHead(requestHead, sendEnd: true)) - XCTAssertEqual( - state.headSent(), - .notifyRequestHeadSendSuccessfully(resumeRequestBodyStream: false, startIdleTimer: true) - ) - - let responseHead = HTTPResponseHead(version: .http1_0, status: .ok, headers: ["content-length": "4"]) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - let responseBody = ByteBuffer(bytes: [1, 2, 3, 4]) - XCTAssertEqual(state.channelRead(.body(responseBody)), .wait) - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.close, .init([responseBody]))) - XCTAssertEqual(state.channelInactive(), .fireChannelInactive) - } - - func testAHTTP1_0ResponseWithKeepAliveHeaderLeadsToConnectionBeingKeptAlive() { - var state = HTTP1ConnectionStateMachine() - XCTAssertEqual(state.channelActive(isWritable: true), .fireChannelActive) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - let newRequestAction = state.runNewRequest(head: requestHead, metadata: metadata) - XCTAssertEqual(newRequestAction, .sendRequestHead(requestHead, sendEnd: true)) - XCTAssertEqual( - state.headSent(), - .notifyRequestHeadSendSuccessfully(resumeRequestBodyStream: false, startIdleTimer: true) - ) - - let responseHead = HTTPResponseHead( - version: .http1_0, - status: .ok, - headers: ["content-length": "4", "connection": "keep-alive"] - ) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - let responseBody = ByteBuffer(bytes: [1, 2, 3, 4]) - XCTAssertEqual(state.channelRead(.body(responseBody)), .wait) - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.informConnectionIsIdle, .init([responseBody]))) - XCTAssertEqual(state.channelInactive(), .fireChannelInactive) - } - - func testAConnectionCloseHeaderInTheResponseLeadsToConnectionCloseAfterRequest() { - var state = HTTP1ConnectionStateMachine() - XCTAssertEqual(state.channelActive(isWritable: false), .fireChannelActive) - XCTAssertEqual(state.writabilityChanged(writable: true), .wait) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - let newRequestAction = state.runNewRequest(head: requestHead, metadata: metadata) - XCTAssertEqual(newRequestAction, .sendRequestHead(requestHead, sendEnd: true)) - XCTAssertEqual( - state.headSent(), - .notifyRequestHeadSendSuccessfully(resumeRequestBodyStream: false, startIdleTimer: true) - ) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok, headers: ["connection": "close"]) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - let responseBody = ByteBuffer(bytes: [1, 2, 3, 4]) - XCTAssertEqual(state.channelRead(.body(responseBody)), .wait) - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.close, .init([responseBody]))) - } - - func testNIOTriggersChannelActiveTwice() { - var state = HTTP1ConnectionStateMachine() - XCTAssertEqual(state.channelActive(isWritable: true), .fireChannelActive) - XCTAssertEqual(state.channelActive(isWritable: true), .wait) - } - - func testIdleConnectionBecomesInactive() { - var state = HTTP1ConnectionStateMachine() - XCTAssertEqual(state.channelActive(isWritable: true), .fireChannelActive) - XCTAssertEqual(state.channelInactive(), .fireChannelInactive) - XCTAssertEqual(state.channelInactive(), .wait) - } - - func testConnectionGoesAwayWhileInRequest() { - var state = HTTP1ConnectionStateMachine() - XCTAssertEqual(state.channelActive(isWritable: true), .fireChannelActive) - - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - let newRequestAction = state.runNewRequest(head: requestHead, metadata: metadata) - XCTAssertEqual(newRequestAction, .sendRequestHead(requestHead, sendEnd: true)) - - XCTAssertEqual(state.channelInactive(), .failRequest(HTTPClientError.remoteConnectionClosed, .none)) - - XCTAssertEqual(state.headSent(), .wait) - } - - func testRequestWasCancelledWhileUploadingData() { - var state = HTTP1ConnectionStateMachine() - XCTAssertEqual(state.channelActive(isWritable: false), .fireChannelActive) - - let requestHead = HTTPRequestHead(version: .http1_1, method: .POST, uri: "/", headers: ["content-length": "4"]) - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(4)) - XCTAssertEqual(state.runNewRequest(head: requestHead, metadata: metadata), .wait) - XCTAssertEqual(state.writabilityChanged(writable: true), .sendRequestHead(requestHead, sendEnd: false)) - XCTAssertEqual( - state.headSent(), - .notifyRequestHeadSendSuccessfully(resumeRequestBodyStream: true, startIdleTimer: false) - ) - - let part0 = IOData.byteBuffer(ByteBuffer(bytes: [0])) - let part1 = IOData.byteBuffer(ByteBuffer(bytes: [1])) - XCTAssertEqual(state.requestStreamPartReceived(part0, promise: nil), .sendBodyPart(part0, nil)) - XCTAssertEqual(state.requestStreamPartReceived(part1, promise: nil), .sendBodyPart(part1, nil)) - XCTAssertEqual( - state.requestCancelled(closeConnection: false), - .failRequest(HTTPClientError.cancelled, .close(nil)) - ) - } - - func testNewRequestAfterErrorHappened() { - var state = HTTP1ConnectionStateMachine() - XCTAssertEqual(state.channelActive(isWritable: false), .fireChannelActive) - struct MyError: Error, Equatable {} - XCTAssertEqual(state.errorHappened(MyError()), .fireChannelError(MyError(), closeConnection: true)) - let requestHead = HTTPRequestHead(version: .http1_1, method: .POST, uri: "/", headers: ["content-length": "4"]) - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(4)) - let action = state.runNewRequest(head: requestHead, metadata: metadata) - guard case .failRequest = action else { - return XCTFail("unexpected action \(action)") - } - } - - func testCancelRequestIsIgnoredWhenConnectionIsIdle() { - var state = HTTP1ConnectionStateMachine() - XCTAssertEqual(state.channelActive(isWritable: true), .fireChannelActive) - XCTAssertEqual(state.requestCancelled(closeConnection: false), .wait, "Should be ignored.") - XCTAssertEqual(state.requestCancelled(closeConnection: true), .close, "Should lead to connection closure.") - XCTAssertEqual( - state.requestCancelled(closeConnection: true), - .wait, - "Should be ignored. Connection is already closing" - ) - XCTAssertEqual(state.channelInactive(), .fireChannelInactive) - XCTAssertEqual( - state.requestCancelled(closeConnection: true), - .wait, - "Should be ignored. Connection is already closed" - ) - } - - func testReadsAreForwardedIfConnectionIsClosing() { - var state = HTTP1ConnectionStateMachine() - XCTAssertEqual(state.channelActive(isWritable: true), .fireChannelActive) - XCTAssertEqual(state.requestCancelled(closeConnection: true), .close) - XCTAssertEqual(state.read(), .read) - XCTAssertEqual(state.channelInactive(), .fireChannelInactive) - XCTAssertEqual(state.read(), .read) - } - - func testChannelReadsAreIgnoredIfConnectionIsClosing() { - var state = HTTP1ConnectionStateMachine() - XCTAssertEqual(state.channelActive(isWritable: true), .fireChannelActive) - XCTAssertEqual(state.requestCancelled(closeConnection: true), .close) - XCTAssertEqual(state.channelRead(.end(nil)), .wait) - XCTAssertEqual(state.channelReadComplete(), .wait) - XCTAssertEqual(state.channelInactive(), .fireChannelInactive) - XCTAssertEqual(state.channelRead(.end(nil)), .wait) - } - - func testRequestIsCancelledWhileWaitingForWritable() { - var state = HTTP1ConnectionStateMachine() - XCTAssertEqual(state.channelActive(isWritable: false), .fireChannelActive) - let requestHead = HTTPRequestHead(version: .http1_1, method: .POST, uri: "/", headers: ["content-length": "4"]) - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(4)) - XCTAssertEqual(state.runNewRequest(head: requestHead, metadata: metadata), .wait) - XCTAssertEqual( - state.requestCancelled(closeConnection: false), - .failRequest(HTTPClientError.cancelled, .informConnectionIsIdle) - ) - } - - func testConnectionIsClosedIfErrorHappensWhileInRequest() { - var state = HTTP1ConnectionStateMachine() - XCTAssertEqual(state.channelActive(isWritable: true), .fireChannelActive) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - let newRequestAction = state.runNewRequest(head: requestHead, metadata: metadata) - XCTAssertEqual(newRequestAction, .sendRequestHead(requestHead, sendEnd: true)) - XCTAssertEqual( - state.headSent(), - .notifyRequestHeadSendSuccessfully(resumeRequestBodyStream: false, startIdleTimer: true) - ) - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - XCTAssertEqual(state.channelRead(.body(ByteBuffer(string: "Hello world!\n"))), .wait) - XCTAssertEqual(state.channelRead(.body(ByteBuffer(string: "Foo Bar!\n"))), .wait) - let decompressionError = NIOHTTPDecompression.DecompressionError.limit - XCTAssertEqual(state.errorHappened(decompressionError), .failRequest(decompressionError, .close(nil))) - } - - func testConnectionIsClosedAfterSwitchingProtocols() { - var state = HTTP1ConnectionStateMachine() - XCTAssertEqual(state.channelActive(isWritable: true), .fireChannelActive) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - let newRequestAction = state.runNewRequest(head: requestHead, metadata: metadata) - XCTAssertEqual(newRequestAction, .sendRequestHead(requestHead, sendEnd: true)) - XCTAssertEqual( - state.headSent(), - .notifyRequestHeadSendSuccessfully(resumeRequestBodyStream: false, startIdleTimer: true) - ) - let responseHead = HTTPResponseHead(version: .http1_1, status: .switchingProtocols) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.close, [])) - } - - func testWeDontCrashAfterEarlyHintsAndConnectionClose() { - var state = HTTP1ConnectionStateMachine() - XCTAssertEqual(state.channelActive(isWritable: true), .fireChannelActive) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - let newRequestAction = state.runNewRequest(head: requestHead, metadata: metadata) - XCTAssertEqual(newRequestAction, .sendRequestHead(requestHead, sendEnd: true)) - XCTAssertEqual( - state.headSent(), - .notifyRequestHeadSendSuccessfully(resumeRequestBodyStream: false, startIdleTimer: true) - ) - let responseHead = HTTPResponseHead( - version: .http1_1, - status: .init(statusCode: 103, reasonPhrase: "Early Hints") - ) - XCTAssertEqual(state.channelRead(.head(responseHead)), .wait) - XCTAssertEqual(state.channelInactive(), .failRequest(HTTPClientError.remoteConnectionClosed, .none)) - } - - func testWeDontCrashInRaceBetweenSchedulingNewRequestAndConnectionClose() { - var state = HTTP1ConnectionStateMachine() - XCTAssertEqual(state.channelActive(isWritable: true), .fireChannelActive) - XCTAssertEqual(state.channelInactive(), .fireChannelInactive) - - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - let newRequestAction = state.runNewRequest(head: requestHead, metadata: metadata) - guard case .failRequest(let error, .none) = newRequestAction else { - return XCTFail("Unexpected test case") - } - XCTAssertEqual(error as? HTTPClientError, .remoteConnectionClosed) - } -} - -extension HTTP1ConnectionStateMachine.Action: Equatable { - public static func == (lhs: Self, rhs: Self) -> Bool { - switch (lhs, rhs) { - case (.fireChannelActive, .fireChannelActive): - return true - - case (.fireChannelInactive, .fireChannelInactive): - return true - case (.fireChannelError(_, let lhsCloseConnection), .fireChannelError(_, let rhsCloseConnection)): - return lhsCloseConnection == rhsCloseConnection - - case (.sendRequestHead(let lhsHead, let lhsStartBody), .sendRequestHead(let rhsHead, let rhsStartBody)): - return lhsHead == rhsHead && lhsStartBody == rhsStartBody - - case ( - .notifyRequestHeadSendSuccessfully(let lhsResumeRequestBodyStream, let lhsStartIdleTimer), - .notifyRequestHeadSendSuccessfully(let rhsResumeRequestBodyStream, let rhsStartIdleTimer) - ): - return lhsResumeRequestBodyStream == rhsResumeRequestBodyStream && lhsStartIdleTimer == rhsStartIdleTimer - - case (.sendBodyPart(let lhsData, let lhsPromise), .sendBodyPart(let rhsData, let rhsPromise)): - return lhsData == rhsData && lhsPromise?.futureResult == rhsPromise?.futureResult - - case (.sendRequestEnd, .sendRequestEnd): - return true - - case (.pauseRequestBodyStream, .pauseRequestBodyStream): - return true - case (.resumeRequestBodyStream, .resumeRequestBodyStream): - return true - - case ( - .forwardResponseHead(let lhsHead, let lhsPauseRequestBodyStream), - .forwardResponseHead(let rhsHead, let rhsPauseRequestBodyStream) - ): - return lhsHead == rhsHead && lhsPauseRequestBodyStream == rhsPauseRequestBodyStream - - case (.forwardResponseBodyParts(let lhsData), .forwardResponseBodyParts(let rhsData)): - return lhsData == rhsData - - case ( - .succeedRequest(let lhsFinalAction, let lhsFinalBuffer), - .succeedRequest(let rhsFinalAction, let rhsFinalBuffer) - ): - return lhsFinalAction == rhsFinalAction && lhsFinalBuffer == rhsFinalBuffer - - case (.failRequest(_, let lhsFinalAction), .failRequest(_, let rhsFinalAction)): - return lhsFinalAction == rhsFinalAction - - case (.read, .read): - return true - - case (.close, .close): - return true - - case (.wait, .wait): - return true - - default: - return false - } - } -} - -extension HTTP1ConnectionStateMachine.Action.FinalSuccessfulStreamAction: Equatable { - public static func == ( - lhs: HTTP1ConnectionStateMachine.Action.FinalSuccessfulStreamAction, - rhs: HTTP1ConnectionStateMachine.Action.FinalSuccessfulStreamAction - ) -> Bool { - switch (lhs, rhs) { - case (.close, .close): - return true - case (sendRequestEnd(let lhsPromise, let lhsShouldClose), sendRequestEnd(let rhsPromise, let rhsShouldClose)): - return lhsPromise?.futureResult == rhsPromise?.futureResult && lhsShouldClose == rhsShouldClose - case (informConnectionIsIdle, informConnectionIsIdle): - return true - default: - return false - } - } -} - -extension HTTP1ConnectionStateMachine.Action.FinalFailedStreamAction: Equatable { - public static func == ( - lhs: HTTP1ConnectionStateMachine.Action.FinalFailedStreamAction, - rhs: HTTP1ConnectionStateMachine.Action.FinalFailedStreamAction - ) -> Bool { - switch (lhs, rhs) { - case (.close(let lhsPromise), .close(let rhsPromise)): - return lhsPromise?.futureResult == rhsPromise?.futureResult - case (.informConnectionIsIdle, .informConnectionIsIdle): - return true - case (.failWritePromise(let lhsPromise), .failWritePromise(let rhsPromise)): - return lhsPromise?.futureResult == rhsPromise?.futureResult - case (.none, .none): - return true - - default: - return false - } - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTP1ConnectionTests.swift b/Tests/AsyncHTTPClientTests/HTTP1ConnectionTests.swift deleted file mode 100644 index 53001b64b..000000000 --- a/Tests/AsyncHTTPClientTests/HTTP1ConnectionTests.swift +++ /dev/null @@ -1,918 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOEmbedded -import NIOHTTP1 -import NIOHTTPCompression -import NIOPosix -import NIOTestUtils -import XCTest - -@testable import AsyncHTTPClient - -class HTTP1ConnectionTests: XCTestCase { - func testCreateNewConnectionWithDecompression() { - let embedded = EmbeddedChannel() - let logger = Logger(label: "test.http1.connection") - - XCTAssertNoThrow(try embedded.connect(to: SocketAddress(ipAddress: "127.0.0.1", port: 3000)).wait()) - - var connection: HTTP1Connection? - XCTAssertNoThrow( - connection = try HTTP1Connection.start( - channel: embedded, - connectionID: 0, - delegate: MockHTTP1ConnectionDelegate(), - decompression: .enabled(limit: .ratio(4)), - logger: logger - ) - ) - - XCTAssertNotNil(try embedded.pipeline.syncOperations.handler(type: HTTPRequestEncoder.self)) - XCTAssertNotNil( - try embedded.pipeline.syncOperations.handler(type: ByteToMessageHandler.self) - ) - XCTAssertNotNil(try embedded.pipeline.syncOperations.handler(type: NIOHTTPResponseDecompressor.self)) - - XCTAssertNoThrow(try connection?.sendableView.close().wait()) - embedded.embeddedEventLoop.run() - XCTAssert(!embedded.isActive) - } - - func testCreateNewConnectionWithoutDecompression() { - let embedded = EmbeddedChannel() - let logger = Logger(label: "test.http1.connection") - - XCTAssertNoThrow(try embedded.connect(to: SocketAddress(ipAddress: "127.0.0.1", port: 3000)).wait()) - - XCTAssertNoThrow( - try HTTP1Connection.start( - channel: embedded, - connectionID: 0, - delegate: MockHTTP1ConnectionDelegate(), - decompression: .disabled, - logger: logger - ) - ) - - XCTAssertNotNil(try embedded.pipeline.syncOperations.handler(type: HTTPRequestEncoder.self)) - XCTAssertNotNil( - try embedded.pipeline.syncOperations.handler(type: ByteToMessageHandler.self) - ) - XCTAssertThrowsError(try embedded.pipeline.syncOperations.handler(type: NIOHTTPResponseDecompressor.self)) { - error in - XCTAssertEqual(error as? ChannelPipelineError, .notFound) - } - } - - func testCreateNewConnectionFailureClosedIO() { - let embedded = EmbeddedChannel() - - XCTAssertNoThrow(try embedded.connect(to: SocketAddress(ipAddress: "127.0.0.1", port: 3000)).wait()) - XCTAssertNoThrow(try embedded.close().wait()) - // to really destroy the channel we need to tick once - embedded.embeddedEventLoop.run() - let logger = Logger(label: "test.http1.connection") - - XCTAssertThrowsError( - try HTTP1Connection.start( - channel: embedded, - connectionID: 0, - delegate: MockHTTP1ConnectionDelegate(), - decompression: .disabled, - logger: logger - ) - ) - } - - func testGETRequest() { - let elg = MultiThreadedEventLoopGroup(numberOfThreads: 2) - let clientEL = elg.next() - let serverEL = elg.next() - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - let server = NIOHTTP1TestServer(group: serverEL) - defer { XCTAssertNoThrow(try server.stop()) } - - let logger = Logger(label: "test") - let delegate = MockHTTP1ConnectionDelegate(closePromise: clientEL.makePromise()) - - let connection = try! ClientBootstrap(group: clientEL) - .connect(to: .init(ipAddress: "127.0.0.1", port: server.serverPort)) - .flatMapThrowing { - try HTTP1Connection.start( - channel: $0, - connectionID: 0, - delegate: delegate, - decompression: .disabled, - logger: logger - ).sendableView - } - .wait() - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow( - maybeRequest = try HTTPClient.Request( - url: "http://localhost/hello/swift", - method: .POST, - body: .stream(contentLength: 4) { writer -> EventLoopFuture in - @Sendable func recursive(count: UInt8, promise: EventLoopPromise) { - guard count < 4 else { - return promise.succeed(()) - } - - writer.write(.byteBuffer(ByteBuffer(bytes: [count]))).whenComplete { result in - switch result { - case .failure(let error): - XCTFail("Unexpected error: \(error)") - case .success: - recursive(count: count + 1, promise: promise) - } - } - } - - let promise = clientEL.makePromise(of: Void.self) - recursive(count: 0, promise: promise) - return promise.futureResult - } - ) - ) - - guard let request = maybeRequest else { - return XCTFail("Expected to have a connection and a request") - } - - let task = HTTPClient.Task(eventLoop: clientEL, logger: logger) - - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: clientEL), - task: task, - redirectHandler: nil, - connectionDeadline: .now() + .seconds(60), - requestOptions: .forTests(), - delegate: ResponseAccumulator(request: request) - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag.") } - connection.executeRequest(requestBag) - - XCTAssertNoThrow( - try server.receiveHeadAndVerify { head in - XCTAssertEqual(head.method, .POST) - XCTAssertEqual(head.uri, "/hello/swift") - XCTAssertEqual(head.headers["content-length"].first, "4") - } - ) - - var received: UInt8 = 0 - while received < 4 { - XCTAssertNoThrow( - try server.receiveBodyAndVerify { body in - var body = body - while let read = body.readInteger(as: UInt8.self) { - XCTAssertEqual(received, read) - received += 1 - } - } - ) - } - XCTAssertEqual(received, 4) - XCTAssertNoThrow(try server.receiveEnd()) - - XCTAssertNoThrow(try server.writeOutbound(.head(.init(version: .http1_1, status: .ok)))) - XCTAssertNoThrow(try server.writeOutbound(.body(.byteBuffer(ByteBuffer(bytes: [0, 1, 2, 3]))))) - XCTAssertNoThrow(try server.writeOutbound(.end(nil))) - - var response: HTTPClient.Response? - XCTAssertNoThrow(response = try task.futureResult.wait()) - - XCTAssertEqual(response?.body, ByteBuffer(bytes: [0, 1, 2, 3])) - - // connection is closed - XCTAssertNoThrow(try XCTUnwrap(delegate.closePromise).futureResult.wait()) - } - - func testConnectionClosesOnCloseHeader() { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - let eventLoop = eventLoopGroup.next() - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let httpBin = HTTPBin(handlerFactory: { _ in SuddenlySendsCloseHeaderChannelHandler(closeOnRequest: 1) }) - - var maybeChannel: Channel? - - XCTAssertNoThrow( - maybeChannel = try ClientBootstrap(group: eventLoop).connect(host: "localhost", port: httpBin.port).wait() - ) - let connectionDelegate = MockConnectionDelegate() - let logger = Logger(label: "test") - var maybeConnection: HTTP1Connection.SendableView? - XCTAssertNoThrow( - maybeConnection = try eventLoop.submit { [maybeChannel] in - try HTTP1Connection.start( - channel: XCTUnwrap(maybeChannel), - connectionID: 0, - delegate: connectionDelegate, - decompression: .disabled, - logger: logger - ).sendableView - }.wait() - ) - guard let connection = maybeConnection else { return XCTFail("Expected to have a connection here") } - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "http://localhost/(httpBin.port)/")) - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseAccumulator(request: request) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: eventLoopGroup.next()), - task: .init(eventLoop: eventLoopGroup.next(), logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - connection.executeRequest(requestBag) - - var response: HTTPClient.Response? - XCTAssertNoThrow(response = try requestBag.task.futureResult.wait()) - XCTAssertEqual(response?.status, .ok) - XCTAssertEqual(connectionDelegate.hitConnectionReleased, 0) - XCTAssertNoThrow(try XCTUnwrap(maybeChannel).closeFuture.wait()) - XCTAssertEqual(connectionDelegate.hitConnectionClosed, 1) - - // we need to wait a small amount of time to see the connection close on the server - try! eventLoop.scheduleTask(in: .milliseconds(200)) {}.futureResult.wait() - XCTAssertEqual(httpBin.activeConnections, 0) - } - - func testConnectionClosesOnRandomlyAppearingCloseHeader() { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - let eventLoop = eventLoopGroup.next() - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let closeOnRequest = (30...100).randomElement()! - let httpBin = HTTPBin(handlerFactory: { _ in - SuddenlySendsCloseHeaderChannelHandler(closeOnRequest: closeOnRequest) - }) - - var maybeChannel: Channel? - - XCTAssertNoThrow( - maybeChannel = try ClientBootstrap(group: eventLoop).connect(host: "localhost", port: httpBin.port).wait() - ) - let connectionDelegate = MockConnectionDelegate() - let logger = Logger(label: "test") - var maybeConnection: HTTP1Connection.SendableView? - XCTAssertNoThrow( - maybeConnection = try eventLoop.submit { [maybeChannel] in - try HTTP1Connection.start( - channel: XCTUnwrap(maybeChannel), - connectionID: 0, - delegate: connectionDelegate, - decompression: .disabled, - logger: logger - ).sendableView - }.wait() - ) - guard let connection = maybeConnection else { return XCTFail("Expected to have a connection here") } - - var counter = 0 - while true { - counter += 1 - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "http://localhost/(httpBin.port)/")) - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseAccumulator(request: request) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: eventLoopGroup.next()), - task: .init(eventLoop: eventLoopGroup.next(), logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { - return XCTFail("Expected to be able to create a request bag") - } - - connection.executeRequest(requestBag) - - var response: HTTPClient.Response? - XCTAssertNoThrow(response = try requestBag.task.futureResult.wait()) - XCTAssertEqual(response?.status, .ok) - - if response?.headers.first(name: "connection") == "close" { - break // the loop - } else { - XCTAssertEqual(httpBin.activeConnections, 1) - XCTAssertEqual(connectionDelegate.hitConnectionReleased, counter) - } - } - - XCTAssertNoThrow(try XCTUnwrap(maybeChannel).closeFuture.wait()) - XCTAssertEqual(connectionDelegate.hitConnectionClosed, 1) - XCTAssertFalse(try XCTUnwrap(maybeChannel).isActive) - - XCTAssertEqual(counter, closeOnRequest) - XCTAssertEqual(connectionDelegate.hitConnectionClosed, 1) - XCTAssertEqual( - connectionDelegate.hitConnectionReleased, - counter - 1, - "If a close header is received connection release is not triggered." - ) - - // we need to wait a small amount of time to see the connection close on the server - try! eventLoop.scheduleTask(in: .milliseconds(200)) {}.futureResult.wait() - XCTAssertEqual(httpBin.activeConnections, 0) - } - - func testConnectionClosesAfterTheRequestWithoutHavingSentAnCloseHeader() { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - let eventLoop = eventLoopGroup.next() - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let httpBin = HTTPBin(handlerFactory: { _ in AfterRequestCloseConnectionChannelHandler() }) - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - var maybeChannel: Channel? - - XCTAssertNoThrow( - maybeChannel = try ClientBootstrap(group: eventLoop).connect(host: "localhost", port: httpBin.port).wait() - ) - let connectionDelegate = MockConnectionDelegate() - let logger = Logger(label: "test") - var maybeConnection: HTTP1Connection.SendableView? - XCTAssertNoThrow( - maybeConnection = try eventLoop.submit { [maybeChannel] in - try HTTP1Connection.start( - channel: XCTUnwrap(maybeChannel), - connectionID: 0, - delegate: connectionDelegate, - decompression: .disabled, - logger: logger - ).sendableView - }.wait() - ) - guard let connection = maybeConnection else { return XCTFail("Expected to have a connection here") } - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "http://localhost/(httpBin.port)/")) - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseAccumulator(request: request) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: eventLoopGroup.next()), - task: .init(eventLoop: eventLoopGroup.next(), logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - connection.executeRequest(requestBag) - - var response: HTTPClient.Response? - XCTAssertNoThrow(response = try requestBag.task.futureResult.wait()) - XCTAssertEqual(response?.status, .ok) - XCTAssertEqual(connectionDelegate.hitConnectionReleased, 1) - - XCTAssertNoThrow(try XCTUnwrap(maybeChannel).closeFuture.wait()) - XCTAssertEqual(connectionDelegate.hitConnectionClosed, 1) - } - - func testConnectionIsClosedAfterSwitchingProtocols() { - let embedded = EmbeddedChannel() - let logger = Logger(label: "test.http1.connection") - - XCTAssertNoThrow(try embedded.connect(to: SocketAddress(ipAddress: "127.0.0.1", port: 3000)).wait()) - - var maybeConnection: HTTP1Connection? - let connectionDelegate = MockConnectionDelegate() - XCTAssertNoThrow( - maybeConnection = try HTTP1Connection.start( - channel: embedded, - connectionID: 0, - delegate: connectionDelegate, - decompression: .enabled(limit: .ratio(4)), - logger: logger - ) - ) - guard let connection = maybeConnection else { return XCTFail("Expected to have a connection at this point.") } - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "http://swift.org/")) - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseAccumulator(request: request) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - connection.sendableView.executeRequest(requestBag) - - XCTAssertNoThrow(try embedded.readOutbound(as: ByteBuffer.self)) // head - XCTAssertNoThrow(try embedded.readOutbound(as: ByteBuffer.self)) // end - - let responseString = """ - HTTP/1.1 101 Switching Protocols\r\n\ - Upgrade: websocket\r\n\ - Sec-WebSocket-Accept: xAMUK7/Il9bLRFJrikq6mm8CNZI=\r\n\ - Connection: upgrade\r\n\ - date: Mon, 27 Sep 2021 17:53:14 GMT\r\n\ - \r\n\ - \r\nfoo bar baz - """ - - XCTAssertTrue(embedded.isActive) - XCTAssertEqual(connectionDelegate.hitConnectionClosed, 0) - XCTAssertEqual(connectionDelegate.hitConnectionReleased, 0) - XCTAssertNoThrow(try embedded.writeInbound(ByteBuffer(string: responseString))) - XCTAssertFalse(embedded.isActive) - (embedded.eventLoop as! EmbeddedEventLoop).run() // tick once to run futures. - XCTAssertEqual(connectionDelegate.hitConnectionClosed, 1) - XCTAssertEqual(connectionDelegate.hitConnectionReleased, 0) - - var response: HTTPClient.Response? - XCTAssertNoThrow(response = try requestBag.task.futureResult.wait()) - XCTAssertEqual(response?.status, .switchingProtocols) - XCTAssertEqual(response?.headers.count, 4) - XCTAssertEqual(response?.body, nil) - } - - func testConnectionDropAfterEarlyHints() { - let embedded = EmbeddedChannel() - let logger = Logger(label: "test.http1.connection") - - XCTAssertNoThrow(try embedded.connect(to: SocketAddress(ipAddress: "127.0.0.1", port: 3000)).wait()) - - var maybeConnection: HTTP1Connection? - let connectionDelegate = MockConnectionDelegate() - XCTAssertNoThrow( - maybeConnection = try HTTP1Connection.start( - channel: embedded, - connectionID: 0, - delegate: connectionDelegate, - decompression: .enabled(limit: .ratio(4)), - logger: logger - ) - ) - guard let connection = maybeConnection else { return XCTFail("Expected to have a connection at this point.") } - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "http://swift.org/")) - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseAccumulator(request: request) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - connection.sendableView.executeRequest(requestBag) - - XCTAssertNoThrow(try embedded.readOutbound(as: ByteBuffer.self)) // head - XCTAssertNoThrow(try embedded.readOutbound(as: ByteBuffer.self)) // end - - let responseString = """ - HTTP/1.1 103 Early Hints\r\n\ - date: Mon, 27 Sep 2021 17:53:14 GMT\r\n\ - \r\n\ - \r\n - """ - - XCTAssertTrue(embedded.isActive) - XCTAssertEqual(connectionDelegate.hitConnectionClosed, 0) - XCTAssertEqual(connectionDelegate.hitConnectionReleased, 0) - XCTAssertNoThrow(try embedded.writeInbound(ByteBuffer(string: responseString))) - - XCTAssertTrue(embedded.isActive, "The connection remains active after the informational response head") - XCTAssertNoThrow(try embedded.close().wait(), "the connection was closed") - - embedded.embeddedEventLoop.run() // tick once to run futures. - XCTAssertEqual(connectionDelegate.hitConnectionClosed, 1) - XCTAssertEqual(connectionDelegate.hitConnectionReleased, 0) - - XCTAssertThrowsError(try requestBag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .remoteConnectionClosed) - } - } - - func testConnectionIsClosedIfResponseIsReceivedBeforeRequest() { - let embedded = EmbeddedChannel() - let logger = Logger(label: "test.http1.connection") - - XCTAssertNoThrow(try embedded.connect(to: SocketAddress(ipAddress: "127.0.0.1", port: 0)).wait()) - - let connectionDelegate = MockConnectionDelegate() - XCTAssertNoThrow( - try HTTP1Connection.start( - channel: embedded, - connectionID: 0, - delegate: connectionDelegate, - decompression: .enabled(limit: .ratio(4)), - logger: logger - ) - ) - - let responseString = """ - HTTP/1.1 200 OK\r\n\ - date: Mon, 27 Sep 2021 17:53:14 GMT\r\n\ - \r\n\ - \r\n - """ - - XCTAssertEqual(connectionDelegate.hitConnectionClosed, 0) - XCTAssertEqual(connectionDelegate.hitConnectionReleased, 0) - - XCTAssertThrowsError(try embedded.writeInbound(ByteBuffer(string: responseString))) { - XCTAssertEqual($0 as? NIOHTTPDecoderError, .unsolicitedResponse) - } - XCTAssertFalse(embedded.isActive) - (embedded.eventLoop as! EmbeddedEventLoop).run() // tick once to run futures. - XCTAssertEqual(connectionDelegate.hitConnectionClosed, 1) - XCTAssertEqual(connectionDelegate.hitConnectionReleased, 0) - } - - func testDoubleHTTPResponseLine() { - let embedded = EmbeddedChannel() - let logger = Logger(label: "test.http1.connection") - - XCTAssertNoThrow(try embedded.connect(to: SocketAddress(ipAddress: "127.0.0.1", port: 0)).wait()) - - var maybeConnection: HTTP1Connection? - let connectionDelegate = MockConnectionDelegate() - XCTAssertNoThrow( - maybeConnection = try HTTP1Connection.start( - channel: embedded, - connectionID: 0, - delegate: connectionDelegate, - decompression: .enabled(limit: .ratio(4)), - logger: logger - ) - ) - guard let connection = maybeConnection else { return XCTFail("Expected to have a connection at this point.") } - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "http://swift.org/")) - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseAccumulator(request: request) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - connection.sendableView.executeRequest(requestBag) - - let responseString = """ - HTTP/1.0 200 OK\r\n\ - HTTP/1.0 200 OK\r\n\r\n - """ - - XCTAssertNoThrow(try embedded.readOutbound(as: ByteBuffer.self)) // head - XCTAssertNoThrow(try embedded.readOutbound(as: ByteBuffer.self)) // end - - XCTAssertEqual(connectionDelegate.hitConnectionClosed, 0) - XCTAssertEqual(connectionDelegate.hitConnectionReleased, 0) - XCTAssertNoThrow(try embedded.writeInbound(ByteBuffer(string: responseString))) - XCTAssertFalse(embedded.isActive) - (embedded.eventLoop as! EmbeddedEventLoop).run() // tick once to run futures. - XCTAssertEqual(connectionDelegate.hitConnectionClosed, 1) - XCTAssertEqual(connectionDelegate.hitConnectionReleased, 0) - } - - // In order to test backpressure we need to make sure that reads will not happen - // until the backpressure promise is succeeded. Since we cannot guarantee when - // messages will be delivered to a client pipeline and we need this test to be - // fast (no waiting for arbitrary amounts of time), we do the following. - // First, we enforce NIO to send us only 1 byte at a time. Then we send a message - // of 4 bytes. This will guarantee that if we see first byte of the message, other - // bytes a ready to be read as well. This will allow us to test if subsequent reads - // are waiting for backpressure promise. - func testDownloadStreamingBackpressure() { - final class BackpressureTestDelegate: HTTPClientResponseDelegate { - typealias Response = Void - - private struct State: Sendable { - var reads = 0 - var channel: Channel? - } - - private let state = NIOLockedValueBox(State()) - - var reads: Int { - self.state.withLockedValue { $0.reads } - } - - let backpressurePromise: EventLoopPromise - let messageReceived: EventLoopPromise - - init(eventLoop: EventLoop) { - self.backpressurePromise = eventLoop.makePromise() - self.messageReceived = eventLoop.makePromise() - } - - func willExecuteOnChannel(_ channel: Channel) { - self.state.withLockedValue { - $0.channel = channel - } - } - - func didReceiveHead(task: HTTPClient.Task, _ head: HTTPResponseHead) -> EventLoopFuture { - task.futureResult.eventLoop.makeSucceededVoidFuture() - } - - func didReceiveBodyPart(task: HTTPClient.Task, _ buffer: ByteBuffer) -> EventLoopFuture { - // We count a number of reads received. - self.state.withLockedValue { - $0.reads += 1 - } - // We need to notify the test when first byte of the message is arrived. - self.messageReceived.succeed(()) - return self.backpressurePromise.futureResult - } - - func didFinishRequest(task: HTTPClient.Task) throws {} - } - - final class WriteAfterFutureSucceedsHandler: ChannelInboundHandler { - typealias InboundIn = HTTPServerRequestPart - typealias OutboundOut = HTTPServerResponsePart - - let endFuture: EventLoopFuture - - init(endFuture: EventLoopFuture) { - self.endFuture = endFuture - } - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - switch self.unwrapInboundIn(data) { - case .head: - let head = HTTPResponseHead(version: HTTPVersion(major: 1, minor: 1), status: .ok) - context.writeAndFlush(wrapOutboundOut(.head(head)), promise: nil) - case .body: - // ignore - break - case .end: - let buffer = context.channel.allocator.buffer(string: "1234") - context.writeAndFlush(self.wrapOutboundOut(.body(.byteBuffer(buffer))), promise: nil) - - self.endFuture.hop(to: context.eventLoop).assumeIsolated().whenSuccess { - context.writeAndFlush(Self.wrapOutboundOut(.end(nil)), promise: nil) - } - } - } - } - - let logger = Logger(label: "test") - - // cannot test with NIOTS as `maxMessagesPerRead` is not supported - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - let requestEventLoop = eventLoopGroup.next() - let backpressureDelegate = BackpressureTestDelegate(eventLoop: requestEventLoop) - - let httpBin = HTTPBin { _ in - WriteAfterFutureSucceedsHandler( - endFuture: backpressureDelegate.backpressurePromise.futureResult - ) - } - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - var maybeChannel: Channel? - XCTAssertNoThrow( - maybeChannel = try ClientBootstrap(group: eventLoopGroup) - .channelOption(ChannelOptions.maxMessagesPerRead, value: 1) - .channelOption(ChannelOptions.recvAllocator, value: FixedSizeRecvByteBufferAllocator(capacity: 1)) - .connect(host: "localhost", port: httpBin.port) - .wait() - ) - guard let channel = maybeChannel else { return XCTFail("Expected to have a channel at this point") } - let connectionDelegate = MockConnectionDelegate() - var maybeConnection: HTTP1Connection.SendableView? - XCTAssertNoThrow( - maybeConnection = try channel.eventLoop.submit { - try HTTP1Connection.start( - channel: channel, - connectionID: 0, - delegate: connectionDelegate, - decompression: .disabled, - logger: logger - ).sendableView - }.wait() - ) - guard let connection = maybeConnection else { return XCTFail("Expected to have a connection at this point") } - - var maybeRequestBag: RequestBag? - - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: HTTPClient.Request(url: "http://localhost/(httpBin.port)/custom"), - eventLoopPreference: .delegate(on: requestEventLoop), - task: .init(eventLoop: requestEventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: backpressureDelegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - backpressureDelegate.willExecuteOnChannel(connection.channel) - - connection.executeRequest(requestBag) - - let requestFuture = requestBag.task.futureResult - - // Send 4 bytes, but only one should be received until the backpressure promise is succeeded. - - // Now we wait until message is delivered to client channel pipeline - XCTAssertNoThrow(try backpressureDelegate.messageReceived.futureResult.wait()) - XCTAssertEqual(backpressureDelegate.reads, 1) - - // Succeed the backpressure promise. - backpressureDelegate.backpressurePromise.succeed(()) - XCTAssertNoThrow(try requestFuture.wait()) - - // At this point all other bytes should be delivered. - XCTAssertEqual(backpressureDelegate.reads, 4) - } -} - -final class MockHTTP1ConnectionDelegate: HTTP1ConnectionDelegate { - let releasePromise: EventLoopPromise? - let closePromise: EventLoopPromise? - - init(releasePromise: EventLoopPromise? = nil, closePromise: EventLoopPromise? = nil) { - self.releasePromise = releasePromise - self.closePromise = closePromise - } - - func http1ConnectionReleased(_: HTTPConnectionPool.Connection.ID) { - self.releasePromise?.succeed(()) - } - - func http1ConnectionClosed(_: HTTPConnectionPool.Connection.ID) { - self.closePromise?.succeed(()) - } -} - -/// A channel handler that sends a connection close header but does not close the connection. -class SuddenlySendsCloseHeaderChannelHandler: ChannelInboundHandler { - typealias InboundIn = HTTPServerRequestPart - typealias OutboundOut = HTTPServerResponsePart - - var counter = 1 - let closeOnRequest: Int - - init(closeOnRequest: Int) { - self.closeOnRequest = closeOnRequest - } - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - switch self.unwrapInboundIn(data) { - case .head(let head): - XCTAssertLessThanOrEqual(self.counter, self.closeOnRequest) - XCTAssertTrue(head.headers.contains(name: "host")) - XCTAssertEqual(head.method, .GET) - case .body: - break - case .end: - if self.closeOnRequest == self.counter { - context.write( - self.wrapOutboundOut( - .head(.init(version: .http1_1, status: .ok, headers: ["connection": "close"])) - ), - promise: nil - ) - context.write(self.wrapOutboundOut(.end(nil)), promise: nil) - context.flush() - self.counter += 1 - } else { - context.write(self.wrapOutboundOut(.head(.init(version: .http1_1, status: .ok))), promise: nil) - context.write(self.wrapOutboundOut(.end(nil)), promise: nil) - context.flush() - self.counter += 1 - } - } - } -} - -/// A channel handler that closes a connection after a successful request -class AfterRequestCloseConnectionChannelHandler: ChannelInboundHandler { - typealias InboundIn = HTTPServerRequestPart - typealias OutboundOut = HTTPServerResponsePart - - init() {} - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - switch self.unwrapInboundIn(data) { - case .head(let head): - XCTAssertTrue(head.headers.contains(name: "host")) - XCTAssertEqual(head.method, .GET) - case .body: - break - case .end: - context.write(self.wrapOutboundOut(.head(.init(version: .http1_1, status: .ok))), promise: nil) - context.write(self.wrapOutboundOut(.end(nil)), promise: nil) - context.flush() - - context.eventLoop.assumeIsolated().scheduleTask(in: .milliseconds(20)) { - context.close(promise: nil) - } - } - } -} - -final class MockConnectionDelegate: HTTP1ConnectionDelegate { - private let counts = NIOLockedValueBox(Counts()) - - private struct Counts: Sendable { - var hitConnectionReleased = 0 - var hitConnectionClosed = 0 - } - - var hitConnectionReleased: Int { - self.counts.withLockedValue { $0.hitConnectionReleased } - } - - var hitConnectionClosed: Int { - self.counts.withLockedValue { $0.hitConnectionClosed } - } - - init() {} - - func http1ConnectionReleased(_: HTTPConnectionPool.Connection.ID) { - self.counts.withLockedValue { - $0.hitConnectionReleased += 1 - } - } - - func http1ConnectionClosed(_: HTTPConnectionPool.Connection.ID) { - self.counts.withLockedValue { - $0.hitConnectionClosed += 1 - } - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTP1ProxyConnectHandlerTests.swift b/Tests/AsyncHTTPClientTests/HTTP1ProxyConnectHandlerTests.swift deleted file mode 100644 index d75865da2..000000000 --- a/Tests/AsyncHTTPClientTests/HTTP1ProxyConnectHandlerTests.swift +++ /dev/null @@ -1,211 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOEmbedded -import NIOHTTP1 -import XCTest - -@testable import AsyncHTTPClient - -class HTTP1ProxyConnectHandlerTests: XCTestCase { - func testProxyConnectWithoutAuthorizationSuccess() { - let embedded = EmbeddedChannel() - defer { XCTAssertNoThrow(try embedded.finish(acceptAlreadyClosed: false)) } - - let socketAddress = try! SocketAddress.makeAddressResolvingHost("localhost", port: 0) - XCTAssertNoThrow(try embedded.connect(to: socketAddress).wait()) - - let proxyConnectHandler = HTTP1ProxyConnectHandler( - targetHost: "swift.org", - targetPort: 443, - proxyAuthorization: .none, - deadline: .now() + .seconds(10) - ) - - XCTAssertNoThrow(try embedded.pipeline.syncOperations.addHandler(proxyConnectHandler)) - - var maybeHead: HTTPClientRequestPart? - XCTAssertNoThrow(maybeHead = try embedded.readOutbound(as: HTTPClientRequestPart.self)) - guard case .some(.head(let head)) = maybeHead else { - return XCTFail("Expected the proxy connect handler to first send a http head part") - } - - XCTAssertEqual(head.method, .CONNECT) - XCTAssertEqual(head.uri, "swift.org:443") - XCTAssertEqual(head.headers["host"].first, "swift.org") - XCTAssertNil(head.headers["proxy-authorization"].first) - XCTAssertEqual(try embedded.readOutbound(as: HTTPClientRequestPart.self), .end(nil)) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.head(responseHead))) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.end(nil))) - - XCTAssertNoThrow(try XCTUnwrap(proxyConnectHandler.proxyEstablishedFuture).wait()) - } - - func testProxyConnectWithAuthorization() { - let embedded = EmbeddedChannel() - - let socketAddress = try! SocketAddress.makeAddressResolvingHost("localhost", port: 0) - XCTAssertNoThrow(try embedded.connect(to: socketAddress).wait()) - - let proxyConnectHandler = HTTP1ProxyConnectHandler( - targetHost: "swift.org", - targetPort: 443, - proxyAuthorization: .basic(credentials: "abc123"), - deadline: .now() + .seconds(10) - ) - - XCTAssertNoThrow(try embedded.pipeline.syncOperations.addHandler(proxyConnectHandler)) - - var maybeHead: HTTPClientRequestPart? - XCTAssertNoThrow(maybeHead = try embedded.readOutbound(as: HTTPClientRequestPart.self)) - guard case .some(.head(let head)) = maybeHead else { - return XCTFail("Expected the proxy connect handler to first send a http head part") - } - - XCTAssertEqual(head.method, .CONNECT) - XCTAssertEqual(head.uri, "swift.org:443") - XCTAssertEqual(head.headers["host"].first, "swift.org") - XCTAssertEqual(head.headers["proxy-authorization"].first, "Basic abc123") - XCTAssertEqual(try embedded.readOutbound(as: HTTPClientRequestPart.self), .end(nil)) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.head(responseHead))) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.end(nil))) - - XCTAssertNoThrow(try XCTUnwrap(proxyConnectHandler.proxyEstablishedFuture).wait()) - } - - func testProxyConnectWithoutAuthorizationFailure500() { - let embedded = EmbeddedChannel() - - let socketAddress = try! SocketAddress.makeAddressResolvingHost("localhost", port: 0) - XCTAssertNoThrow(try embedded.connect(to: socketAddress).wait()) - - let proxyConnectHandler = HTTP1ProxyConnectHandler( - targetHost: "swift.org", - targetPort: 443, - proxyAuthorization: .none, - deadline: .now() + .seconds(10) - ) - - XCTAssertNoThrow(try embedded.pipeline.syncOperations.addHandler(proxyConnectHandler)) - - var maybeHead: HTTPClientRequestPart? - XCTAssertNoThrow(maybeHead = try embedded.readOutbound(as: HTTPClientRequestPart.self)) - guard case .some(.head(let head)) = maybeHead else { - return XCTFail("Expected the proxy connect handler to first send a http head part") - } - - XCTAssertEqual(head.method, .CONNECT) - XCTAssertEqual(head.uri, "swift.org:443") - XCTAssertEqual(head.headers["host"].first, "swift.org") - XCTAssertNil(head.headers["proxy-authorization"].first) - XCTAssertEqual(try embedded.readOutbound(as: HTTPClientRequestPart.self), .end(nil)) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .internalServerError) - // answering with 500 should lead to a triggered error in pipeline - XCTAssertThrowsError(try embedded.writeInbound(HTTPClientResponsePart.head(responseHead))) { - XCTAssertEqual($0 as? HTTPClientError, .invalidProxyResponse) - } - XCTAssertFalse(embedded.isActive, "Channel should be closed in response to the error") - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.end(nil))) - - XCTAssertThrowsError(try XCTUnwrap(proxyConnectHandler.proxyEstablishedFuture).wait()) { - XCTAssertEqual($0 as? HTTPClientError, .invalidProxyResponse) - } - } - - func testProxyConnectWithoutAuthorizationButAuthorizationNeeded() { - let embedded = EmbeddedChannel() - - let socketAddress = try! SocketAddress.makeAddressResolvingHost("localhost", port: 0) - XCTAssertNoThrow(try embedded.connect(to: socketAddress).wait()) - - let proxyConnectHandler = HTTP1ProxyConnectHandler( - targetHost: "swift.org", - targetPort: 443, - proxyAuthorization: .none, - deadline: .now() + .seconds(10) - ) - - XCTAssertNoThrow(try embedded.pipeline.syncOperations.addHandler(proxyConnectHandler)) - - var maybeHead: HTTPClientRequestPart? - XCTAssertNoThrow(maybeHead = try embedded.readOutbound(as: HTTPClientRequestPart.self)) - guard case .some(.head(let head)) = maybeHead else { - return XCTFail("Expected the proxy connect handler to first send a http head part") - } - - XCTAssertEqual(head.method, .CONNECT) - XCTAssertEqual(head.uri, "swift.org:443") - XCTAssertEqual(head.headers["host"].first, "swift.org") - XCTAssertNil(head.headers["proxy-authorization"].first) - XCTAssertEqual(try embedded.readOutbound(as: HTTPClientRequestPart.self), .end(nil)) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .proxyAuthenticationRequired) - // answering with 500 should lead to a triggered error in pipeline - XCTAssertThrowsError(try embedded.writeInbound(HTTPClientResponsePart.head(responseHead))) { - XCTAssertEqual($0 as? HTTPClientError, .proxyAuthenticationRequired) - } - XCTAssertFalse(embedded.isActive, "Channel should be closed in response to the error") - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.end(nil))) - - XCTAssertThrowsError(try XCTUnwrap(proxyConnectHandler.proxyEstablishedFuture).wait()) { - XCTAssertEqual($0 as? HTTPClientError, .proxyAuthenticationRequired) - } - } - - func testProxyConnectReceivesBody() { - let embedded = EmbeddedChannel() - - let socketAddress = try! SocketAddress.makeAddressResolvingHost("localhost", port: 0) - XCTAssertNoThrow(try embedded.connect(to: socketAddress).wait()) - - let proxyConnectHandler = HTTP1ProxyConnectHandler( - targetHost: "swift.org", - targetPort: 443, - proxyAuthorization: .none, - deadline: .now() + .seconds(10) - ) - - XCTAssertNoThrow(try embedded.pipeline.syncOperations.addHandler(proxyConnectHandler)) - - var maybeHead: HTTPClientRequestPart? - XCTAssertNoThrow(maybeHead = try embedded.readOutbound(as: HTTPClientRequestPart.self)) - guard case .some(.head(let head)) = maybeHead else { - return XCTFail("Expected the proxy connect handler to first send a http head part") - } - - XCTAssertEqual(head.method, .CONNECT) - XCTAssertEqual(head.uri, "swift.org:443") - XCTAssertEqual(head.headers["host"].first, "swift.org") - XCTAssertEqual(try embedded.readOutbound(as: HTTPClientRequestPart.self), .end(nil)) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.head(responseHead))) - // answering with a body should lead to a triggered error in pipeline - XCTAssertThrowsError(try embedded.writeInbound(HTTPClientResponsePart.body(ByteBuffer(bytes: [0, 1, 2, 3])))) { - XCTAssertEqual($0 as? HTTPClientError, .invalidProxyResponse) - } - XCTAssertEqual(embedded.isActive, false) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.end(nil))) - - XCTAssertThrowsError(try XCTUnwrap(proxyConnectHandler.proxyEstablishedFuture).wait()) { - XCTAssertEqual($0 as? HTTPClientError, .invalidProxyResponse) - } - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTP2ClientRequestHandlerTests.swift b/Tests/AsyncHTTPClientTests/HTTP2ClientRequestHandlerTests.swift deleted file mode 100644 index 71f7f3d1a..000000000 --- a/Tests/AsyncHTTPClientTests/HTTP2ClientRequestHandlerTests.swift +++ /dev/null @@ -1,579 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOCore -import NIOEmbedded -import NIOHTTP1 -import XCTest - -@testable import AsyncHTTPClient - -class HTTP2ClientRequestHandlerTests: XCTestCase { - func testResponseBackpressure() { - let embedded = EmbeddedChannel() - let readEventHandler = ReadEventHitHandler() - let requestHandler = HTTP2ClientRequestHandler(eventLoop: embedded.eventLoop) - let logger = Logger(label: "test") - - XCTAssertNoThrow(try embedded.pipeline.syncOperations.addHandlers([readEventHandler, requestHandler])) - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "http://localhost/")) - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseBackpressureDelegate(eventLoop: embedded.eventLoop) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - embedded.write(requestBag, promise: nil) - XCTAssertNoThrow(try embedded.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait()) - - XCTAssertNoThrow( - try embedded.receiveHeadAndVerify { - XCTAssertEqual($0.method, .GET) - XCTAssertEqual($0.uri, "/") - XCTAssertEqual($0.headers.first(name: "host"), "localhost") - } - ) - XCTAssertEqual(try embedded.readOutbound(as: HTTPClientRequestPart.self), .end(nil)) - - let responseHead = HTTPResponseHead( - version: .http1_1, - status: .ok, - headers: HTTPHeaders([("content-length", "12")]) - ) - - XCTAssertEqual(readEventHandler.readHitCounter, 0) - embedded.read() - XCTAssertEqual(readEventHandler.readHitCounter, 1) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.head(responseHead))) - - let part0 = ByteBuffer(bytes: 0...3) - let part1 = ByteBuffer(bytes: 4...7) - let part2 = ByteBuffer(bytes: 8...11) - - // part 0. Demand first, read second - XCTAssertEqual(readEventHandler.readHitCounter, 1) - let part0Future = delegate.next() - XCTAssertEqual(readEventHandler.readHitCounter, 1) - embedded.read() - XCTAssertEqual(readEventHandler.readHitCounter, 2) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.body(part0))) - XCTAssertEqual(try part0Future.wait(), part0) - - // part 1. read first, demand second - - XCTAssertEqual(readEventHandler.readHitCounter, 2) - embedded.read() - XCTAssertEqual(readEventHandler.readHitCounter, 2) - let part1Future = delegate.next() - XCTAssertEqual(readEventHandler.readHitCounter, 3) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.body(part1))) - XCTAssertEqual(try part1Future.wait(), part1) - - // part 2. Demand first, read second - XCTAssertEqual(readEventHandler.readHitCounter, 3) - let part2Future = delegate.next() - XCTAssertEqual(readEventHandler.readHitCounter, 3) - embedded.read() - XCTAssertEqual(readEventHandler.readHitCounter, 4) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.body(part2))) - XCTAssertEqual(try part2Future.wait(), part2) - - // end. read first, demand second - XCTAssertEqual(readEventHandler.readHitCounter, 4) - embedded.read() - XCTAssertEqual(readEventHandler.readHitCounter, 4) - let endFuture = delegate.next() - XCTAssertEqual(readEventHandler.readHitCounter, 5) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.end(nil))) - XCTAssertEqual(try endFuture.wait(), .none) - - XCTAssertNoThrow(try requestBag.task.futureResult.wait()) - } - - func testWriteBackpressure() { - let embedded = EmbeddedChannel() - let requestHandler = HTTP2ClientRequestHandler(eventLoop: embedded.eventLoop) - XCTAssertNoThrow(try embedded.pipeline.syncOperations.addHandler(requestHandler)) - let logger = Logger(label: "test") - - let testWriter = TestBackpressureWriter(eventLoop: embedded.eventLoop, parts: 50) - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow( - maybeRequest = try HTTPClient.Request( - url: "http://localhost/", - method: .POST, - body: .stream(contentLength: 100) { writer in - testWriter.start(writer: writer) - } - ) - ) - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseAccumulator(request: request) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(idleReadTimeout: .milliseconds(200)), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - embedded.isWritable = false - XCTAssertNoThrow(try embedded.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait()) - embedded.write(requestBag, promise: nil) - - // the handler only writes once the channel is writable - XCTAssertEqual(try embedded.readOutbound(as: HTTPClientRequestPart.self), .none) - embedded.isWritable = true - testWriter.writabilityChanged(true) - embedded.pipeline.fireChannelWritabilityChanged() - - XCTAssertNoThrow( - try embedded.receiveHeadAndVerify { - XCTAssertEqual($0.method, .POST) - XCTAssertEqual($0.uri, "/") - XCTAssertEqual($0.headers.first(name: "host"), "localhost") - XCTAssertEqual($0.headers.first(name: "content-length"), "100") - } - ) - - // the next body write will be executed once we tick the el. before we make the channel - // unwritable - - for index in 0..<50 { - embedded.isWritable = false - testWriter.writabilityChanged(false) - embedded.pipeline.fireChannelWritabilityChanged() - - XCTAssertEqual(testWriter.written, index) - - embedded.embeddedEventLoop.run() - - XCTAssertNoThrow( - try embedded.receiveBodyAndVerify { - XCTAssertEqual($0.readableBytes, 2) - } - ) - - XCTAssertEqual(testWriter.written, index + 1) - - embedded.isWritable = true - testWriter.writabilityChanged(true) - embedded.pipeline.fireChannelWritabilityChanged() - } - - embedded.embeddedEventLoop.run() - XCTAssertNoThrow(try embedded.receiveEnd()) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.head(responseHead))) - embedded.read() - - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.end(nil))) - XCTAssertNoThrow(try requestBag.task.futureResult.wait()) - } - - func testIdleReadTimeout() { - let embedded = EmbeddedChannel() - let readEventHandler = ReadEventHitHandler() - let requestHandler = HTTP2ClientRequestHandler(eventLoop: embedded.eventLoop) - XCTAssertNoThrow(try embedded.pipeline.syncOperations.addHandlers([readEventHandler, requestHandler])) - XCTAssertNoThrow(try embedded.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait()) - let logger = Logger(label: "test") - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "http://localhost/")) - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseBackpressureDelegate(eventLoop: embedded.eventLoop) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(idleReadTimeout: .milliseconds(200)), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - embedded.write(requestBag, promise: nil) - - XCTAssertNoThrow( - try embedded.receiveHeadAndVerify { - XCTAssertEqual($0.method, .GET) - XCTAssertEqual($0.uri, "/") - XCTAssertEqual($0.headers.first(name: "host"), "localhost") - } - ) - XCTAssertNoThrow(try embedded.receiveEnd()) - - let responseHead = HTTPResponseHead( - version: .http1_1, - status: .ok, - headers: HTTPHeaders([("content-length", "12")]) - ) - - XCTAssertEqual(readEventHandler.readHitCounter, 0) - embedded.read() - XCTAssertEqual(readEventHandler.readHitCounter, 1) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.head(responseHead))) - - // not sending anything after the head should lead to request fail and connection close - - embedded.embeddedEventLoop.advanceTime(by: .milliseconds(250)) - - XCTAssertThrowsError(try requestBag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .readTimeout) - } - } - - func testIdleReadTimeoutIsCanceledIfRequestIsCanceled() { - let embedded = EmbeddedChannel() - let readEventHandler = ReadEventHitHandler() - let requestHandler = HTTP2ClientRequestHandler(eventLoop: embedded.eventLoop) - XCTAssertNoThrow(try embedded.pipeline.syncOperations.addHandlers([readEventHandler, requestHandler])) - XCTAssertNoThrow(try embedded.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait()) - let logger = Logger(label: "test") - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "http://localhost/")) - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseBackpressureDelegate(eventLoop: embedded.eventLoop) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(idleReadTimeout: .milliseconds(200)), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - embedded.write(requestBag, promise: nil) - - XCTAssertNoThrow( - try embedded.receiveHeadAndVerify { - XCTAssertEqual($0.method, .GET) - XCTAssertEqual($0.uri, "/") - XCTAssertEqual($0.headers.first(name: "host"), "localhost") - } - ) - XCTAssertNoThrow(try embedded.receiveEnd()) - - let responseHead = HTTPResponseHead( - version: .http1_1, - status: .ok, - headers: HTTPHeaders([("content-length", "12")]) - ) - - XCTAssertEqual(readEventHandler.readHitCounter, 0) - embedded.read() - XCTAssertEqual(readEventHandler.readHitCounter, 1) - XCTAssertNoThrow(try embedded.writeInbound(HTTPClientResponsePart.head(responseHead))) - - // canceling the request - requestBag.fail(HTTPClientError.cancelled) - XCTAssertThrowsError(try requestBag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .cancelled) - } - - // the idle read timeout should be cleared because we canceled the request - // therefore advancing the time should not trigger a crash - embedded.embeddedEventLoop.advanceTime(by: .milliseconds(250)) - } - - func testIdleWriteTimeout() { - let embedded = EmbeddedChannel() - let requestHandler = HTTP2ClientRequestHandler(eventLoop: embedded.eventLoop) - XCTAssertNoThrow(try embedded.pipeline.syncOperations.addHandlers([requestHandler])) - XCTAssertNoThrow(try embedded.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait()) - let logger = Logger(label: "test") - - let testWriter = TestBackpressureWriter(eventLoop: embedded.eventLoop, parts: 5) - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow( - maybeRequest = try HTTPClient.Request( - url: "http://localhost/", - method: .POST, - body: .stream(contentLength: 10) { writer in - // Advance time by more than the idle write timeout (that's 1 millisecond) to trigger the timeout. - embedded.embeddedEventLoop.advanceTime(by: .milliseconds(2)) - return testWriter.start(writer: writer) - } - ) - ) - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseBackpressureDelegate(eventLoop: embedded.eventLoop) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(idleWriteTimeout: .milliseconds(1)), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - embedded.isWritable = true - testWriter.writabilityChanged(true) - embedded.pipeline.fireChannelWritabilityChanged() - embedded.write(requestBag, promise: nil) - - XCTAssertThrowsError(try requestBag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .writeTimeout) - } - } - - func testIdleWriteTimeoutWritabilityChanged() { - let embedded = EmbeddedChannel() - let readEventHandler = ReadEventHitHandler() - let requestHandler = HTTP2ClientRequestHandler(eventLoop: embedded.eventLoop) - XCTAssertNoThrow(try embedded.pipeline.syncOperations.addHandlers([readEventHandler, requestHandler])) - XCTAssertNoThrow(try embedded.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait()) - let logger = Logger(label: "test") - - let testWriter = TestBackpressureWriter(eventLoop: embedded.eventLoop, parts: 5) - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow( - maybeRequest = try HTTPClient.Request( - url: "http://localhost/", - method: .POST, - body: .stream(contentLength: 10) { writer in - embedded.isWritable = false - embedded.pipeline.fireChannelWritabilityChanged() - // This should not trigger any errors or timeouts, because the timer isn't running - // as the channel is not writable. - embedded.embeddedEventLoop.advanceTime(by: .milliseconds(20)) - - // Now that the channel will become writable, this should trigger a timeout. - embedded.isWritable = true - embedded.pipeline.fireChannelWritabilityChanged() - embedded.embeddedEventLoop.advanceTime(by: .milliseconds(2)) - - return testWriter.start(writer: writer) - } - ) - ) - - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseAccumulator(request: request) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(idleWriteTimeout: .milliseconds(1)), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - embedded.isWritable = true - testWriter.writabilityChanged(true) - embedded.pipeline.fireChannelWritabilityChanged() - embedded.write(requestBag, promise: nil) - - XCTAssertThrowsError(try requestBag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .writeTimeout) - } - } - - func testIdleWriteTimeoutIsCanceledIfRequestIsCanceled() { - let embedded = EmbeddedChannel() - let readEventHandler = ReadEventHitHandler() - let requestHandler = HTTP2ClientRequestHandler(eventLoop: embedded.eventLoop) - XCTAssertNoThrow(try embedded.pipeline.syncOperations.addHandlers([readEventHandler, requestHandler])) - XCTAssertNoThrow(try embedded.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait()) - let logger = Logger(label: "test") - - let testWriter = TestBackpressureWriter(eventLoop: embedded.eventLoop, parts: 5) - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow( - maybeRequest = try HTTPClient.Request( - url: "http://localhost/", - method: .POST, - body: .stream(contentLength: 2) { writer in - testWriter.start(writer: writer, expectedErrors: [HTTPClientError.cancelled]) - } - ) - ) - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseBackpressureDelegate(eventLoop: embedded.eventLoop) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(idleWriteTimeout: .milliseconds(1)), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag") } - - embedded.isWritable = true - testWriter.writabilityChanged(true) - embedded.pipeline.fireChannelWritabilityChanged() - embedded.write(requestBag, promise: nil) - - // canceling the request - requestBag.fail(HTTPClientError.cancelled) - XCTAssertThrowsError(try requestBag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .cancelled) - } - - // the idle read timeout should be cleared because we canceled the request - // therefore advancing the time should not trigger a crash - embedded.embeddedEventLoop.advanceTime(by: .milliseconds(250)) - } - - func testWriteHTTPHeadFails() { - struct WriteError: Error, Equatable {} - - class FailWriteHandler: ChannelOutboundHandler { - typealias OutboundIn = HTTPClientRequestPart - typealias OutboundOut = HTTPClientRequestPart - - func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise?) { - let error = WriteError() - promise?.fail(error) - context.fireErrorCaught(error) - } - } - - let bodies: [HTTPClient.Body?] = [ - .none, - .some(.byteBuffer(ByteBuffer(string: "hello world"))), - ] - - for body in bodies { - let embeddedEventLoop = EmbeddedEventLoop() - let requestHandler = HTTP2ClientRequestHandler(eventLoop: embeddedEventLoop) - let embedded = EmbeddedChannel(handlers: [FailWriteHandler(), requestHandler], loop: embeddedEventLoop) - - let logger = Logger(label: "test") - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "http://localhost/", method: .POST, body: body)) - guard let request = maybeRequest else { return XCTFail("Expected to be able to create a request") } - - let delegate = ResponseAccumulator(request: request) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embedded.eventLoop), - task: .init(eventLoop: embedded.eventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(idleReadTimeout: .milliseconds(200)), - delegate: delegate - ) - ) - guard let requestBag = maybeRequestBag else { - return XCTFail("Expected to be able to create a request bag") - } - - embedded.isWritable = false - XCTAssertNoThrow(try embedded.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait()) - embedded.write(requestBag, promise: nil) - - // the handler only writes once the channel is writable - XCTAssertEqual(try embedded.readOutbound(as: HTTPClientRequestPart.self), .none) - XCTAssertTrue(embedded.isActive) - embedded.isWritable = true - embedded.pipeline.fireChannelWritabilityChanged() - - XCTAssertThrowsError(try requestBag.task.futureResult.wait()) { - XCTAssertEqual($0 as? WriteError, WriteError()) - } - - XCTAssertFalse(embedded.isActive) - } - } - - func testChannelBecomesNonWritableDuringHeaderWrite() throws { - final class ChangeWritabilityOnFlush: ChannelOutboundHandler { - typealias OutboundIn = Any - func flush(context: ChannelHandlerContext) { - context.flush() - (context.channel as! EmbeddedChannel).isWritable = false - context.fireChannelWritabilityChanged() - } - } - let eventLoopGroup = EmbeddedEventLoopGroup(loops: 1) - let eventLoop = eventLoopGroup.next() as! EmbeddedEventLoop - let handler = HTTP2ClientRequestHandler( - eventLoop: eventLoop - ) - let channel = EmbeddedChannel( - handlers: [ - ChangeWritabilityOnFlush(), - handler, - ], - loop: eventLoop - ) - try channel.connect(to: .init(ipAddress: "127.0.0.1", port: 80)).wait() - - // non empty body is important to trigger this bug as we otherwise finish the request in a single flush - let request = MockHTTPExecutableRequest( - framingMetadata: RequestFramingMetadata(connectionClose: false, body: .fixedSize(1)), - raiseErrorIfUnimplementedMethodIsCalled: false - ) - channel.writeAndFlush(request, promise: nil) - XCTAssertEqual(request.events.map(\.kind), [.willExecuteRequest, .requestHeadSent]) - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTP2ClientTests.swift b/Tests/AsyncHTTPClientTests/HTTP2ClientTests.swift deleted file mode 100644 index 183a227bd..000000000 --- a/Tests/AsyncHTTPClientTests/HTTP2ClientTests.swift +++ /dev/null @@ -1,591 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import AsyncHTTPClient // NOT @testable - tests that really need @testable go into HTTP2ClientInternalTests.swift -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOFoundationCompat -import NIOHTTP1 -import NIOHTTP2 -import NIOPosix -import NIOSSL -import XCTest - -#if canImport(Network) -import Network -#endif - -class HTTP2ClientTests: XCTestCase { - func makeDefaultHTTPClient( - eventLoopGroupProvider: HTTPClient.EventLoopGroupProvider = .singleton - ) -> HTTPClient { - var config = HTTPClient.Configuration() - config.tlsConfiguration = .clientDefault - config.tlsConfiguration?.certificateVerification = .none - config.httpVersion = .automatic - return HTTPClient( - eventLoopGroupProvider: eventLoopGroupProvider, - configuration: config, - backgroundActivityLogger: Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - ) - } - - func makeClientWithActiveHTTP2Connection( - to bin: HTTPBin, - eventLoopGroupProvider: HTTPClient.EventLoopGroupProvider = .singleton - ) -> HTTPClient { - let client = self.makeDefaultHTTPClient(eventLoopGroupProvider: eventLoopGroupProvider) - var response: HTTPClient.Response? - XCTAssertNoThrow(response = try client.get(url: "https://localhost/(bin.port)/get").wait()) - XCTAssertEqual(.ok, response?.status) - XCTAssertEqual(response?.version, .http2) - return client - } - - func testSimpleGet() { - let bin = HTTPBin(.http2(compress: false)) - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = self.makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - var response: HTTPClient.Response? - XCTAssertNoThrow(response = try client.get(url: "https://localhost/(bin.port)/get").wait()) - - XCTAssertEqual(.ok, response?.status) - XCTAssertEqual(response?.version, .http2) - } - - func testStreamRequestBodyWithoutKnowledgeAboutLength() { - let bin = HTTPBin(.http2(compress: false)) { _ in HTTPEchoHandler() } - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = self.makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - var response: HTTPClient.Response? - let body = HTTPClient.Body.stream(contentLength: nil) { writer in - writer.write(.byteBuffer(ByteBuffer(integer: UInt64(0)))).flatMap { - writer.write(.byteBuffer(ByteBuffer(integer: UInt64(0)))) - } - } - XCTAssertNoThrow(response = try client.post(url: "https://localhost/(bin.port)", body: body).wait()) - - XCTAssertEqual(.ok, response?.status) - XCTAssertEqual(response?.version, .http2) - } - - func testStreamRequestBodyWithFalseKnowledgeAboutLength() { - let bin = HTTPBin(.http2(compress: false)) { _ in HTTPEchoHandler() } - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = self.makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let body = HTTPClient.Body.stream(contentLength: 12) { writer in - writer.write(.byteBuffer(ByteBuffer(integer: UInt64(0)))).flatMap { - writer.write(.byteBuffer(ByteBuffer(integer: UInt64(0)))) - } - } - XCTAssertThrowsError(try client.post(url: "https://localhost/(bin.port)", body: body).wait()) { - XCTAssertEqual($0 as? HTTPClientError, .bodyLengthMismatch) - } - } - - func testConcurrentRequests() { - let bin = HTTPBin(.http2(compress: false)) - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = self.makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let el = client.eventLoopGroup.next() - let requestPromises = (0..<1000).map { _ in - client.get(url: "https://localhost/(bin.port)/get") - .map { result -> Void in - XCTAssertEqual(result.version, .http2) - } - } - XCTAssertNoThrow(try EventLoopFuture.whenAllComplete(requestPromises, on: el).wait()) - } - - func testConcurrentRequestsFromDifferentThreads() { - let bin = HTTPBin(.http2(compress: false)) - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = self.makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let numberOfWorkers = 20 - let numberOfRequestsPerWorkers = 20 - let allWorkersReady = DispatchSemaphore(value: 0) - let allWorkersGo = DispatchSemaphore(value: 0) - let allDone = DispatchGroup() - - let url = "https://localhost/(bin.port)/get" - - var response: HTTPClient.Response? - XCTAssertNoThrow(response = try client.get(url: url).wait()) - - XCTAssertEqual(.ok, response?.status) - XCTAssertEqual(response?.version, .http2) - - for w in 0.. HTTPClient.Response in - XCTAssertTrue(el.inEventLoop) - return response - } - XCTAssertNoThrow(response = try requestPromise.wait()) - - XCTAssertEqual(.ok, response?.status) - XCTAssertEqual(response?.version, .http2) - } - } - go() - } - } - - for _ in 0..] = [] - XCTAssertNoThrow( - results = - try EventLoopFuture - .whenAllComplete(responses, on: clientGroup.next()) - .timeout(after: .seconds(2)) - .wait() - ) - - for result in results { - switch result { - case .success: - XCTFail("Shouldn't succeed") - case .failure(let error): - XCTAssertEqual(error as? HTTPClientError, .cancelled) - } - } - } - - func testCancelingRunningRequest() { - let bin = HTTPBin(.http2(compress: false)) { _ in SendHeaderAndWaitChannelHandler() } - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = self.makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://localhost/(bin.port)")) - guard let request = maybeRequest else { return } - - let taskBox = NIOLockedValueBox?>(nil) - let delegate = HeadReceivedCallback { _ in - // request is definitely running because we just received a head from the server - taskBox.withLockedValue { $0 }!.cancel() - } - let task = client.execute( - request: request, - delegate: delegate - ) - taskBox.withLockedValue { $0 = task } - - XCTAssertThrowsError(try task.futureResult.timeout(after: .seconds(2)).wait()) { - XCTAssertEqualTypeAndValue($0, HTTPClientError.cancelled) - } - } - - func testReadTimeout() { - let bin = HTTPBin(.http2(compress: false)) { _ in SendHeaderAndWaitChannelHandler() } - defer { XCTAssertNoThrow(try bin.shutdown()) } - var config = HTTPClient.Configuration() - var tlsConfig = TLSConfiguration.makeClientConfiguration() - tlsConfig.certificateVerification = .none - config.tlsConfiguration = tlsConfig - config.httpVersion = .automatic - config.timeout.read = .milliseconds(100) - let client = HTTPClient( - eventLoopGroupProvider: .singleton, - configuration: config, - backgroundActivityLogger: Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - ) - defer { XCTAssertNoThrow(try client.syncShutdown()) } - - let response = client.get(url: "https://localhost/(bin.port)") - XCTAssertThrowsError(try response.timeout(after: .seconds(2)).wait()) { error in - XCTAssertEqual(error as? HTTPClientError, .readTimeout) - } - } - - func testH2CanHandleRequestsThatHaveAlreadyHitTheDeadline() { - let bin = HTTPBin(.http2(compress: false)) - defer { XCTAssertNoThrow(try bin.shutdown()) } - var config = HTTPClient.Configuration() - var tlsConfig = TLSConfiguration.makeClientConfiguration() - tlsConfig.certificateVerification = .none - config.tlsConfiguration = tlsConfig - config.httpVersion = .automatic - let client = HTTPClient( - // TODO: Test fails if the provided ELG is a multi-threaded NIOTSEventLoopGroup (probably racy) - eventLoopGroupProvider: .shared(bin.group), - configuration: config, - backgroundActivityLogger: Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - ) - defer { XCTAssertNoThrow(try client.syncShutdown()) } - - var request: HTTPClient.Request? - XCTAssertNoThrow(request = try HTTPClient.Request(url: "https://localhost/(bin.port)")) - - // just to establish an existing connection - XCTAssertNoThrow(try client.execute(request: XCTUnwrap(request)).wait()) - - XCTAssertThrowsError(try client.execute(request: XCTUnwrap(request), deadline: .now() - .seconds(2)).wait()) { - XCTAssertEqual($0 as? HTTPClientError, .deadlineExceeded) - } - } - - func testStressCancelingRunningRequestFromDifferentThreads() { - let bin = HTTPBin(.http2(compress: false)) { _ in SendHeaderAndWaitChannelHandler() } - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = self.makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - let cancelPool = MultiThreadedEventLoopGroup(numberOfThreads: 10) - defer { XCTAssertNoThrow(try cancelPool.syncShutdownGracefully()) } - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://localhost/(bin.port)")) - guard let request = maybeRequest else { return } - - let tasks = (0..<100).map { _ -> HTTPClient.Task in - let taskBox = NIOLockedValueBox?>(nil) - - let delegate = HeadReceivedCallback { _ in - // request is definitely running because we just received a head from the server - cancelPool.next().execute { - // canceling from a different thread - taskBox.withLockedValue { $0 }!.cancel() - } - } - let task = client.execute( - request: request, - delegate: delegate - ) - taskBox.withLockedValue { $0 = task } - return task - } - - for task in tasks { - XCTAssertThrowsError(try task.futureResult.timeout(after: .seconds(2)).wait()) { - XCTAssertEqual($0 as? HTTPClientError, .cancelled) - } - } - } - - func testPlatformConnectErrorIsForwardedOnTimeout() { - let bin = HTTPBin(.http2(compress: false), reusePort: true) - let clientGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2) - let el1 = clientGroup.next() - let el2 = clientGroup.next() - defer { XCTAssertNoThrow(try clientGroup.syncShutdownGracefully()) } - var config = HTTPClient.Configuration() - config.tlsConfiguration = .clientDefault - config.tlsConfiguration?.certificateVerification = .none - config.httpVersion = .automatic - config.timeout.connect = .milliseconds(1000) - let client = HTTPClient( - eventLoopGroupProvider: .shared(clientGroup), - configuration: config, - backgroundActivityLogger: Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - ) - defer { XCTAssertNoThrow(try client.syncShutdown()) } - - var maybeRequest1: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest1 = try HTTPClient.Request(url: "https://localhost/(bin.port)/get")) - guard let request1 = maybeRequest1 else { return } - - let task1 = client.execute( - request: request1, - delegate: ResponseAccumulator(request: request1), - eventLoop: .delegateAndChannel(on: el1) - ) - var response1: ResponseAccumulator.Response? - XCTAssertNoThrow(response1 = try task1.wait()) - - XCTAssertEqual(.ok, response1?.status) - XCTAssertEqual(response1?.version, .http2) - let serverPort = bin.port - - let serverGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try serverGroup.syncShutdownGracefully()) } - var maybeServer: Channel? - XCTAssertNoThrow( - maybeServer = try ServerBootstrap(group: serverGroup) - .serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1) - .serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEPORT), value: 1) - .childChannelInitializer { channel in - channel.close() - } - .childChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1) - .bind(host: "127.0.0.1", port: serverPort) - .wait() - ) - // shutting down the old server closes all connections immediately - XCTAssertNoThrow(try bin.shutdown()) - // client is now in HTTP/2 state and the HTTPBin is closed - guard let server = maybeServer else { return } - defer { XCTAssertNoThrow(try server.close().wait()) } - - var maybeRequest2: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest2 = try HTTPClient.Request(url: "https://localhost/(serverPort)/")) - guard let request2 = maybeRequest2 else { return } - - let task2 = client.execute( - request: request2, - delegate: ResponseAccumulator(request: request2), - eventLoop: .delegateAndChannel(on: el2) - ) - XCTAssertThrowsError(try task2.wait()) { error in - XCTAssertNil( - error as? HTTPClientError, - "error should be some platform specific error that the connection is closed/reset by the other side" - ) - } - } - - func testMassiveDownload() { - let bin = HTTPBin(.http2(compress: false)) - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = self.makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - var response: HTTPClient.Response? - XCTAssertNoThrow(response = try client.get(url: "https://localhost/(bin.port)/mega-chunked").wait()) - - XCTAssertEqual(.ok, response?.status) - XCTAssertEqual(response?.version, .http2) - XCTAssertEqual(response?.body?.readableBytes, 10_000) - } - - func testSimplePost() { - let bin = HTTPBin(.http2(compress: false)) - defer { XCTAssertNoThrow(try bin.shutdown()) } - let client = self.makeDefaultHTTPClient() - defer { XCTAssertNoThrow(try client.syncShutdown()) } - var response: HTTPClient.Response? - XCTAssertNoThrow( - response = try client.post( - url: "https://localhost/(bin.port)/post", - body: .byteBuffer(ByteBuffer(repeating: 0, count: 12345)) - ).wait() - ) - XCTAssertEqual(.ok, response?.status) - XCTAssertEqual(response?.version, .http2) - XCTAssertEqual( - String(buffer: ByteBuffer(repeating: 0, count: 12345)), - try response?.body.map { body in - try JSONDecoder().decode(RequestInfo.self, from: body) - }?.data - ) - } - - func testHugePost() { - // Regression test for https://github.com/swift-server/async-http-client/issues/784 - let group = MultiThreadedEventLoopGroup(numberOfThreads: 2) // This needs to be more than 1! - defer { - XCTAssertNoThrow(try group.syncShutdownGracefully()) - } - var serverH2Settings: HTTP2Settings = HTTP2Settings() - serverH2Settings.append(HTTP2Setting(parameter: .maxFrameSize, value: 16 * 1024 * 1024 - 1)) - serverH2Settings.append(HTTP2Setting(parameter: .initialWindowSize, value: Int(Int32.max))) - let bin = HTTPBin( - .http2(compress: false, settings: serverH2Settings) - ) - defer { XCTAssertNoThrow(try bin.shutdown()) } - var clientConfig = HTTPClient.Configuration() - clientConfig.tlsConfiguration = .clientDefault - clientConfig.tlsConfiguration?.certificateVerification = .none - clientConfig.httpVersion = .automatic - let client = HTTPClient( - eventLoopGroupProvider: .shared(group), - configuration: clientConfig, - backgroundActivityLogger: Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:)) - ) - defer { XCTAssertNoThrow(try client.syncShutdown()) } - - let loop1 = group.next() - let loop2 = group.next() - precondition(loop1 !== loop2, "bug in test setup, need two distinct loops") - - XCTAssertNoThrow( - try client.execute( - request: .init(url: "https://localhost/(bin.port)/get"), - eventLoop: .delegateAndChannel(on: loop1) // This will force the channel to live on `loop1`. - ).wait() - ) - var response: HTTPClient.Response? - let byteCount = 1024 * 1024 * 1024 // 1 GiB (unfortunately it has to be that big to trigger the bug) - XCTAssertNoThrow( - response = try client.execute( - request: HTTPClient.Request( - url: "https://localhost/(bin.port)/post-respond-with-byte-count", - method: .POST, - body: .data(Data(repeating: 0, count: byteCount)) - ), - eventLoop: .delegate(on: loop2) - ).wait() - ) - XCTAssertEqual(.ok, response?.status) - XCTAssertEqual(response?.version, .http2) - XCTAssertEqual( - "\(byteCount)", - try response?.body.map { body in - try JSONDecoder().decode(RequestInfo.self, from: body) - }?.data - ) - } -} - -private final class HeadReceivedCallback: HTTPClientResponseDelegate { - typealias Response = Void - private let didReceiveHeadCallback: @Sendable (HTTPResponseHead) -> Void - init(didReceiveHead: @escaping @Sendable (HTTPResponseHead) -> Void) { - self.didReceiveHeadCallback = didReceiveHead - } - - func didReceiveHead(task: HTTPClient.Task, _ head: HTTPResponseHead) -> EventLoopFuture { - self.didReceiveHeadCallback(head) - return task.eventLoop.makeSucceededVoidFuture() - } - - func didFinishRequest(task: HTTPClient.Task) throws {} -} - -/// sends some headers and waits indefinitely afterwards -private final class SendHeaderAndWaitChannelHandler: ChannelInboundHandler { - typealias InboundIn = HTTPServerRequestPart - typealias OutboundOut = HTTPServerResponsePart - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - let requestPart = self.unwrapInboundIn(data) - switch requestPart { - case .head: - context.writeAndFlush( - self.wrapOutboundOut( - .head( - HTTPResponseHead( - version: HTTPVersion(major: 1, minor: 1), - status: .ok - ) - ) - ), - promise: nil - ) - case .body, .end: - return - } - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTP2ConnectionTests.swift b/Tests/AsyncHTTPClientTests/HTTP2ConnectionTests.swift deleted file mode 100644 index 3244e2b5a..000000000 --- a/Tests/AsyncHTTPClientTests/HTTP2ConnectionTests.swift +++ /dev/null @@ -1,653 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOEmbedded -import NIOHPACK -import NIOHTTP1 -import NIOHTTP2 -import NIOPosix -import NIOSSL -import NIOTestUtils -import XCTest - -@testable import AsyncHTTPClient - -class HTTP2ConnectionTests: XCTestCase { - func testCreateNewConnectionFailureClosedIO() { - let embedded = EmbeddedChannel() - - XCTAssertNoThrow(try embedded.connect(to: SocketAddress(ipAddress: "127.0.0.1", port: 3000)).wait()) - XCTAssertNoThrow(try embedded.close().wait()) - // to really destroy the channel we need to tick once - embedded.embeddedEventLoop.run() - let logger = Logger(label: "test.http2.connection") - - XCTAssertThrowsError( - try HTTP2Connection.start( - channel: embedded, - connectionID: 0, - delegate: TestHTTP2ConnectionDelegate(), - decompression: .disabled, - maximumConnectionUses: nil, - logger: logger - ).map { _ in }.nonisolated().wait() - ) - } - - func testConnectionToleratesShutdownEventsAfterAlreadyClosed() { - let embedded = EmbeddedChannel() - XCTAssertNoThrow(try embedded.connect(to: SocketAddress(ipAddress: "127.0.0.1", port: 3000)).wait()) - - let logger = Logger(label: "test.http2.connection") - let connection = HTTP2Connection( - channel: embedded, - connectionID: 0, - decompression: .disabled, - maximumConnectionUses: nil, - delegate: TestHTTP2ConnectionDelegate(), - logger: logger - ) - let startFuture = connection._start0() - - XCTAssertNoThrow(try embedded.close().wait()) - // to really destroy the channel we need to tick once - embedded.embeddedEventLoop.run() - - XCTAssertThrowsError(try startFuture.wait()) - - // should not crash - connection.sendableView.shutdown() - } - - func testSimpleGetRequest() { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - let eventLoop = eventLoopGroup.next() - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let httpBin = HTTPBin(.http2(compress: false)) - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let connectionCreator = TestConnectionCreator() - let delegate = TestHTTP2ConnectionDelegate() - var maybeHTTP2Connection: HTTP2Connection.SendableView? - XCTAssertNoThrow( - maybeHTTP2Connection = try connectionCreator.createHTTP2Connection( - to: httpBin.port, - delegate: delegate, - on: eventLoop - ) - ) - guard let http2Connection = maybeHTTP2Connection else { - return XCTFail("Expected to have an HTTP2 connection here.") - } - - var maybeRequest: HTTPClient.Request? - var maybeRequestBag: RequestBag? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://localhost/(httpBin.port)")) - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: XCTUnwrap(maybeRequest), - eventLoopPreference: .indifferent, - task: .init(eventLoop: eventLoop, logger: .init(label: "test")), - redirectHandler: nil, - connectionDeadline: .distantFuture, - requestOptions: .forTests(), - delegate: ResponseAccumulator(request: XCTUnwrap(maybeRequest)) - ) - ) - guard let requestBag = maybeRequestBag else { - return XCTFail("Expected to have a request bag at this point") - } - - http2Connection.executeRequest(requestBag) - - XCTAssertEqual(delegate.hitStreamClosed, 0) - var maybeResponse: HTTPClient.Response? - XCTAssertNoThrow(maybeResponse = try requestBag.task.futureResult.wait()) - XCTAssertEqual(maybeResponse?.status, .ok) - XCTAssertEqual(maybeResponse?.version, .http2) - XCTAssertEqual(delegate.hitStreamClosed, 1) - } - - func testEveryDoneRequestLeadsToAStreamAvailableCall() { - class NeverRespondChannelHandler: ChannelInboundHandler { - typealias InboundIn = HTTPServerRequestPart - typealias OutboundOut = HTTPServerResponsePart - - init() {} - - func channelRead(context: ChannelHandlerContext, data: NIOAny) {} - } - - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - let eventLoop = eventLoopGroup.next() - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let httpBin = HTTPBin(.http2(compress: false)) - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let connectionCreator = TestConnectionCreator() - let delegate = TestHTTP2ConnectionDelegate() - var maybeHTTP2Connection: HTTP2Connection.SendableView? - XCTAssertNoThrow( - maybeHTTP2Connection = try connectionCreator.createHTTP2Connection( - to: httpBin.port, - delegate: delegate, - on: eventLoop - ) - ) - guard let http2Connection = maybeHTTP2Connection else { - return XCTFail("Expected to have an HTTP2 connection here.") - } - defer { XCTAssertNoThrow(try http2Connection.close().wait()) } - - var futures = [EventLoopFuture]() - - XCTAssertEqual(delegate.hitStreamClosed, 0) - - for _ in 0..<100 { - var maybeRequest: HTTPClient.Request? - var maybeRequestBag: RequestBag? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://localhost/(httpBin.port)")) - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: XCTUnwrap(maybeRequest), - eventLoopPreference: .indifferent, - task: .init(eventLoop: eventLoop, logger: .init(label: "test")), - redirectHandler: nil, - connectionDeadline: .distantFuture, - requestOptions: .forTests(), - delegate: ResponseAccumulator(request: XCTUnwrap(maybeRequest)) - ) - ) - guard let requestBag = maybeRequestBag else { - return XCTFail("Expected to have a request bag at this point") - } - - http2Connection.executeRequest(requestBag) - - futures.append(requestBag.task.futureResult) - } - - for future in futures { - XCTAssertNoThrow(try future.wait()) - } - - XCTAssertEqual(delegate.hitStreamClosed, 100) - XCTAssertTrue(http2Connection.channel.isActive) - } - - func testCancelAllRunningRequests() { - class NeverRespondChannelHandler: ChannelInboundHandler { - typealias InboundIn = HTTPServerRequestPart - typealias OutboundOut = HTTPServerResponsePart - - init() {} - - func channelRead(context: ChannelHandlerContext, data: NIOAny) {} - } - - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - let eventLoop = eventLoopGroup.next() - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let httpBin = HTTPBin(.http2(compress: false), handlerFactory: { _ in NeverRespondChannelHandler() }) - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let connectionCreator = TestConnectionCreator() - let delegate = TestHTTP2ConnectionDelegate() - var maybeHTTP2Connection: HTTP2Connection.SendableView? - XCTAssertNoThrow( - maybeHTTP2Connection = try connectionCreator.createHTTP2Connection( - to: httpBin.port, - delegate: delegate, - on: eventLoop - ) - ) - guard let http2Connection = maybeHTTP2Connection else { - return XCTFail("Expected to have an HTTP2 connection here.") - } - - var futures = [EventLoopFuture]() - - for _ in 0..<100 { - var maybeRequest: HTTPClient.Request? - var maybeRequestBag: RequestBag? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://localhost/(httpBin.port)")) - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: XCTUnwrap(maybeRequest), - eventLoopPreference: .indifferent, - task: .init(eventLoop: eventLoop, logger: .init(label: "test")), - redirectHandler: nil, - connectionDeadline: .distantFuture, - requestOptions: .forTests(), - delegate: ResponseAccumulator(request: XCTUnwrap(maybeRequest)) - ) - ) - guard let requestBag = maybeRequestBag else { - return XCTFail("Expected to have a request bag at this point") - } - - http2Connection.executeRequest(requestBag) - - XCTAssertEqual(delegate.hitStreamClosed, 0) - - futures.append(requestBag.task.futureResult) - } - - http2Connection.shutdown() - - for future in futures { - XCTAssertThrowsError(try future.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .cancelled) - } - } - - XCTAssertNoThrow(try http2Connection.closeFuture.wait()) - } - - func testChildStreamsAreRemovedFromTheOpenChannelListOnceTheRequestIsDone() { - class SucceedPromiseOnRequestHandler: ChannelInboundHandler { - typealias InboundIn = HTTPServerRequestPart - typealias OutboundOut = HTTPServerResponsePart - - let dataArrivedPromise: EventLoopPromise - let triggerResponseFuture: EventLoopFuture - - init(dataArrivedPromise: EventLoopPromise, triggerResponseFuture: EventLoopFuture) { - self.dataArrivedPromise = dataArrivedPromise - self.triggerResponseFuture = triggerResponseFuture - } - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - self.dataArrivedPromise.succeed(()) - - self.triggerResponseFuture.hop(to: context.eventLoop).assumeIsolated().whenSuccess { - switch self.unwrapInboundIn(data) { - case .head: - context.write(self.wrapOutboundOut(.head(.init(version: .http2, status: .ok))), promise: nil) - context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: nil) - case .body, .end: - break - } - } - } - } - - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - let eventLoop = eventLoopGroup.next() - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let serverReceivedRequestPromise = eventLoop.makePromise(of: Void.self) - let triggerResponsePromise = eventLoop.makePromise(of: Void.self) - let httpBin = HTTPBin(.http2(compress: false)) { _ in - SucceedPromiseOnRequestHandler( - dataArrivedPromise: serverReceivedRequestPromise, - triggerResponseFuture: triggerResponsePromise.futureResult - ) - } - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let connectionCreator = TestConnectionCreator() - let delegate = TestHTTP2ConnectionDelegate() - var maybeHTTP2Connection: HTTP2Connection.SendableView? - XCTAssertNoThrow( - maybeHTTP2Connection = try connectionCreator.createHTTP2Connection( - to: httpBin.port, - delegate: delegate, - on: eventLoop - ) - ) - guard let http2Connection = maybeHTTP2Connection else { - return XCTFail("Expected to have an HTTP2 connection here.") - } - - var maybeRequest: HTTPClient.Request? - var maybeRequestBag: RequestBag? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://localhost/(httpBin.port)")) - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: XCTUnwrap(maybeRequest), - eventLoopPreference: .indifferent, - task: .init(eventLoop: eventLoop, logger: .init(label: "test")), - redirectHandler: nil, - connectionDeadline: .distantFuture, - requestOptions: .forTests(), - delegate: ResponseAccumulator(request: XCTUnwrap(maybeRequest)) - ) - ) - guard let requestBag = maybeRequestBag else { - return XCTFail("Expected to have a request bag at this point") - } - - http2Connection.executeRequest(requestBag) - - XCTAssertNoThrow(try serverReceivedRequestPromise.futureResult.wait()) - var channelCount: Int? - XCTAssertNoThrow( - channelCount = try eventLoop.submit { http2Connection.__forTesting_getStreamChannels().count }.wait() - ) - XCTAssertEqual(channelCount, 1) - triggerResponsePromise.succeed(()) - - XCTAssertNoThrow(try requestBag.task.futureResult.wait()) - - // this is racy. for this reason we allow a couple of tries - var retryCount = 0 - let maxRetries = 1000 - while retryCount < maxRetries { - XCTAssertNoThrow( - channelCount = try eventLoop.submit { http2Connection.__forTesting_getStreamChannels().count }.wait() - ) - if channelCount == 0 { - break - } - retryCount += 1 - } - XCTAssertLessThan(retryCount, maxRetries) - } - - func testServerPushIsDisabled() { - let embedded = EmbeddedChannel() - let logger = Logger(label: "test.http2.connection") - let connection = HTTP2Connection( - channel: embedded, - connectionID: 0, - decompression: .disabled, - maximumConnectionUses: nil, - delegate: TestHTTP2ConnectionDelegate(), - logger: logger - ) - _ = connection._start0() - - let settingsFrame = HTTP2Frame(streamID: 0, payload: .settings(.settings([]))) - XCTAssertNoThrow(try connection.channel.writeAndFlush(settingsFrame).wait()) - - let pushPromiseFrame = HTTP2Frame(streamID: 0, payload: .pushPromise(.init(pushedStreamID: 1, headers: [:]))) - XCTAssertThrowsError(try connection.channel.writeAndFlush(pushPromiseFrame).wait()) { error in - XCTAssertNotNil(error as? NIOHTTP2Errors.PushInViolationOfSetting) - } - } -} - -final class TestConnectionCreator { - enum Error: Swift.Error { - case alreadyCreatingAnotherConnection - case wantedHTTP2ConnectionButGotHTTP1 - case wantedHTTP1ConnectionButGotHTTP2 - } - - enum State { - case idle - case waitingForHTTP1Connection(EventLoopPromise) - case waitingForHTTP2Connection(EventLoopPromise) - } - - private let lock = NIOLockedValueBox(.idle) - - init() {} - - func createHTTP1Connection( - to port: Int, - delegate: HTTP1ConnectionDelegate, - connectionID: HTTPConnectionPool.Connection.ID = 0, - on eventLoop: EventLoop, - logger: Logger = .init(label: "test") - ) throws -> HTTP1Connection.SendableView { - let request = try! HTTPClient.Request(url: "https://localhost/(port)") - - var tlsConfiguration = TLSConfiguration.makeClientConfiguration() - tlsConfiguration.certificateVerification = .none - var config = HTTPClient.Configuration() - config.httpVersion = .automatic - let factory = HTTPConnectionPool.ConnectionFactory( - key: .init(request), - tlsConfiguration: tlsConfiguration, - clientConfiguration: config, - sslContextCache: .init() - ) - - let promise = try self.lock.withLockedValue { state in - guard case .idle = state else { - throw Error.alreadyCreatingAnotherConnection - } - - let promise = eventLoop.makePromise(of: HTTP1Connection.SendableView.self) - state = .waitingForHTTP1Connection(promise) - return promise - } - - factory.makeConnection( - for: self, - connectionID: connectionID, - http1ConnectionDelegate: delegate, - http2ConnectionDelegate: EmptyHTTP2ConnectionDelegate(), - deadline: .now() + .seconds(2), - eventLoop: eventLoop, - logger: logger - ) - - return try promise.futureResult.wait() - } - - func createHTTP2Connection( - to port: Int, - delegate: HTTP2ConnectionDelegate, - connectionID: HTTPConnectionPool.Connection.ID = 0, - on eventLoop: EventLoop, - logger: Logger = .init(label: "test") - ) throws -> HTTP2Connection.SendableView { - let request = try! HTTPClient.Request(url: "https://localhost/(port)") - - var tlsConfiguration = TLSConfiguration.makeClientConfiguration() - tlsConfiguration.certificateVerification = .none - var config = HTTPClient.Configuration() - config.httpVersion = .automatic - let factory = HTTPConnectionPool.ConnectionFactory( - key: .init(request), - tlsConfiguration: tlsConfiguration, - clientConfiguration: config, - sslContextCache: .init() - ) - - let promise = try self.lock.withLockedValue { state in - guard case .idle = state else { - throw Error.alreadyCreatingAnotherConnection - } - - let promise = eventLoop.makePromise(of: HTTP2Connection.SendableView.self) - state = .waitingForHTTP2Connection(promise) - return promise - } - - factory.makeConnection( - for: self, - connectionID: connectionID, - http1ConnectionDelegate: EmptyHTTP1ConnectionDelegate(), - http2ConnectionDelegate: delegate, - deadline: .now() + .seconds(2), - eventLoop: eventLoop, - logger: logger - ) - - return try promise.futureResult.wait() - } -} - -extension TestConnectionCreator: HTTPConnectionRequester { - enum EitherPromiseWrapper: Sendable { - case succeed(EventLoopPromise, SucceedType) - case fail(EventLoopPromise, Error) - - func complete() { - switch self { - case .succeed(let promise, let success): - promise.succeed(success) - case .fail(let promise, let error): - promise.fail(error) - } - } - } - - func http1ConnectionCreated(_ connection: HTTP1Connection.SendableView) { - let wrapper: EitherPromiseWrapper = self.lock - .withLockedValue { state in - - switch state { - case .waitingForHTTP1Connection(let promise): - return .succeed(promise, connection) - - case .waitingForHTTP2Connection(let promise): - return .fail(promise, Error.wantedHTTP2ConnectionButGotHTTP1) - - case .idle: - preconditionFailure("Invalid state: \(state)") - } - } - wrapper.complete() - } - - func http2ConnectionCreated(_ connection: HTTP2Connection.SendableView, maximumStreams: Int) { - let wrapper: EitherPromiseWrapper = self.lock - .withLockedValue { state in - switch state { - case .waitingForHTTP1Connection(let promise): - return .fail(promise, Error.wantedHTTP1ConnectionButGotHTTP2) - - case .waitingForHTTP2Connection(let promise): - return .succeed(promise, connection) - - case .idle: - preconditionFailure("Invalid state: \(state)") - } - } - wrapper.complete() - } - - enum FailPromiseWrapper { - case type1(EventLoopPromise) - case type2(EventLoopPromise) - - func fail(_ error: Swift.Error) { - switch self { - case .type1(let eventLoopPromise): - eventLoopPromise.fail(error) - case .type2(let eventLoopPromise): - eventLoopPromise.fail(error) - } - } - } - - func failedToCreateHTTPConnection(_: HTTPConnectionPool.Connection.ID, error: Swift.Error) { - let wrapper: FailPromiseWrapper = self.lock - .withLockedValue { state in - - switch state { - case .waitingForHTTP1Connection(let promise): - return .type1(promise) - - case .waitingForHTTP2Connection(let promise): - return .type2(promise) - - case .idle: - preconditionFailure("Invalid state: \(state)") - } - } - wrapper.fail(error) - } - - func waitingForConnectivity(_: HTTPConnectionPool.Connection.ID, error: Swift.Error) { - preconditionFailure("TODO") - } -} - -final class TestHTTP2ConnectionDelegate: HTTP2ConnectionDelegate { - var hitStreamClosed: Int { - self.lock.withLockedValue { $0.hitStreamClosed } - } - - var hitGoAwayReceived: Int { - self.lock.withLockedValue { $0.hitGoAwayReceived } - } - - var hitConnectionClosed: Int { - self.lock.withLockedValue { $0.hitConnectionClosed } - } - - var maxStreamSetting: Int { - self.lock.withLockedValue { $0.maxStreamSetting } - } - - private let lock = NIOLockedValueBox(.init()) - private struct Counts { - var hitStreamClosed: Int = 0 - var hitGoAwayReceived: Int = 0 - var hitConnectionClosed: Int = 0 - var maxStreamSetting: Int = 100 - } - - init() {} - - func http2Connection(_: HTTPConnectionPool.Connection.ID, newMaxStreamSetting: Int) {} - - func http2ConnectionStreamClosed(_: HTTPConnectionPool.Connection.ID, availableStreams: Int) { - self.lock.withLockedValue { - $0.hitStreamClosed += 1 - } - } - - func http2ConnectionGoAwayReceived(_: HTTPConnectionPool.Connection.ID) { - self.lock.withLockedValue { - $0.hitGoAwayReceived += 1 - } - } - - func http2ConnectionClosed(_: HTTPConnectionPool.Connection.ID) { - self.lock.withLockedValue { - $0.hitConnectionClosed += 1 - } - } -} - -final class EmptyHTTP2ConnectionDelegate: HTTP2ConnectionDelegate { - func http2Connection(_: HTTPConnectionPool.Connection.ID, newMaxStreamSetting: Int) { - preconditionFailure("Unimplemented") - } - - func http2ConnectionStreamClosed(_: HTTPConnectionPool.Connection.ID, availableStreams: Int) { - preconditionFailure("Unimplemented") - } - - func http2ConnectionGoAwayReceived(_: HTTPConnectionPool.Connection.ID) { - preconditionFailure("Unimplemented") - } - - func http2ConnectionClosed(_: HTTPConnectionPool.Connection.ID) { - preconditionFailure("Unimplemented") - } -} - -final class EmptyHTTP1ConnectionDelegate: HTTP1ConnectionDelegate { - func http1ConnectionReleased(_: HTTPConnectionPool.Connection.ID) { - preconditionFailure("Unimplemented") - } - - func http1ConnectionClosed(_: HTTPConnectionPool.Connection.ID) { - preconditionFailure("Unimplemented") - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTP2IdleHandlerTests.swift b/Tests/AsyncHTTPClientTests/HTTP2IdleHandlerTests.swift deleted file mode 100644 index f2b56daa0..000000000 --- a/Tests/AsyncHTTPClientTests/HTTP2IdleHandlerTests.swift +++ /dev/null @@ -1,373 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOCore -import NIOEmbedded -import NIOHTTP2 -import XCTest - -@testable import AsyncHTTPClient - -class HTTP2IdleHandlerTests: XCTestCase { - func testReceiveSettingsWithMaxConcurrentStreamSetting() { - let delegate = MockHTTP2IdleHandlerDelegate() - let idleHandler = HTTP2IdleHandler(delegate: delegate, logger: Logger(label: "test")) - let embedded = EmbeddedChannel(handlers: [idleHandler]) - XCTAssertNoThrow(try embedded.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait()) - - let settingsFrame = HTTP2Frame( - streamID: 0, - payload: .settings(.settings([.init(parameter: .maxConcurrentStreams, value: 10)])) - ) - XCTAssertEqual(delegate.maxStreams, nil) - XCTAssertNoThrow(try embedded.writeInbound(settingsFrame)) - XCTAssertEqual(delegate.maxStreams, 10) - } - - func testReceiveSettingsWithoutMaxConcurrentStreamSetting() { - let delegate = MockHTTP2IdleHandlerDelegate() - let idleHandler = HTTP2IdleHandler(delegate: delegate, logger: Logger(label: "test")) - let embedded = EmbeddedChannel(handlers: [idleHandler]) - XCTAssertNoThrow(try embedded.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait()) - - let settingsFrame = HTTP2Frame(streamID: 0, payload: .settings(.settings([]))) - XCTAssertEqual(delegate.maxStreams, nil) - XCTAssertNoThrow(try embedded.writeInbound(settingsFrame)) - XCTAssertEqual( - delegate.maxStreams, - 100, - "Expected to assume 100 maxConcurrentConnection, if no setting was present" - ) - } - - func testEmptySettingsDontOverwriteMaxConcurrentStreamSetting() { - let delegate = MockHTTP2IdleHandlerDelegate() - let idleHandler = HTTP2IdleHandler(delegate: delegate, logger: Logger(label: "test")) - let embedded = EmbeddedChannel(handlers: [idleHandler]) - XCTAssertNoThrow(try embedded.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait()) - - let settingsFrame = HTTP2Frame( - streamID: 0, - payload: .settings(.settings([.init(parameter: .maxConcurrentStreams, value: 10)])) - ) - XCTAssertEqual(delegate.maxStreams, nil) - XCTAssertNoThrow(try embedded.writeInbound(settingsFrame)) - XCTAssertEqual(delegate.maxStreams, 10) - - let emptySettings = HTTP2Frame(streamID: 0, payload: .settings(.settings([]))) - XCTAssertNoThrow(try embedded.writeInbound(emptySettings)) - XCTAssertEqual(delegate.maxStreams, 10) - } - - func testOverwriteMaxConcurrentStreamSetting() { - let delegate = MockHTTP2IdleHandlerDelegate() - let idleHandler = HTTP2IdleHandler(delegate: delegate, logger: Logger(label: "test")) - let embedded = EmbeddedChannel(handlers: [idleHandler]) - XCTAssertNoThrow(try embedded.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait()) - - let settingsFrame = HTTP2Frame( - streamID: 0, - payload: .settings(.settings([.init(parameter: .maxConcurrentStreams, value: 10)])) - ) - XCTAssertEqual(delegate.maxStreams, nil) - XCTAssertNoThrow(try embedded.writeInbound(settingsFrame)) - XCTAssertEqual(delegate.maxStreams, 10) - - let emptySettings = HTTP2Frame( - streamID: 0, - payload: .settings(.settings([.init(parameter: .maxConcurrentStreams, value: 20)])) - ) - XCTAssertNoThrow(try embedded.writeInbound(emptySettings)) - XCTAssertEqual(delegate.maxStreams, 20) - } - - func testGoAwayReceivedBeforeSettings() { - let delegate = MockHTTP2IdleHandlerDelegate() - let idleHandler = HTTP2IdleHandler(delegate: delegate, logger: Logger(label: "test")) - let embedded = EmbeddedChannel(handlers: [idleHandler]) - XCTAssertNoThrow(try embedded.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait()) - - let randomStreamID = HTTP2StreamID((0..() - - for i in 0..<(1...100).randomElement()! { - let streamID = HTTP2StreamID(i) - let event = NIOHTTP2StreamCreatedEvent( - streamID: streamID, - localInitialWindowSize: nil, - remoteInitialWindowSize: nil - ) - embedded.pipeline.fireUserInboundEventTriggered(event) - openStreams.insert(streamID) - } - - embedded.pipeline.triggerUserOutboundEvent(HTTPConnectionEvent.shutdownRequested, promise: nil) - XCTAssertTrue(embedded.isActive) - - while let streamID = openStreams.randomElement() { - openStreams.remove(streamID) - - let event = StreamClosedEvent(streamID: streamID, reason: nil) - XCTAssertTrue(embedded.isActive) - embedded.pipeline.fireUserInboundEventTriggered(event) - if openStreams.isEmpty { - XCTAssertFalse(embedded.isActive) - } else { - XCTAssertTrue(embedded.isActive) - } - } - } - - func testGoAwayWhileThereAreOpenStreams() { - let delegate = MockHTTP2IdleHandlerDelegate() - let idleHandler = HTTP2IdleHandler(delegate: delegate, logger: Logger(label: "test")) - let embedded = EmbeddedChannel(handlers: [idleHandler]) - XCTAssertNoThrow(try embedded.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait()) - - let settingsFrame = HTTP2Frame( - streamID: 0, - payload: .settings(.settings([.init(parameter: .maxConcurrentStreams, value: 10)])) - ) - XCTAssertEqual(delegate.maxStreams, nil) - XCTAssertNoThrow(try embedded.writeInbound(settingsFrame)) - XCTAssertEqual(delegate.maxStreams, 10) - - var openStreams = Set() - - for i in 0..<(1...100).randomElement()! { - let streamID = HTTP2StreamID(i) - let event = NIOHTTP2StreamCreatedEvent( - streamID: streamID, - localInitialWindowSize: nil, - remoteInitialWindowSize: nil - ) - embedded.pipeline.fireUserInboundEventTriggered(event) - openStreams.insert(streamID) - } - - let goAwayStreamID = HTTP2StreamID(openStreams.count) - let goAwayFrame = HTTP2Frame( - streamID: goAwayStreamID, - payload: .goAway(lastStreamID: 0, errorCode: .http11Required, opaqueData: nil) - ) - XCTAssertEqual(delegate.goAwayReceived, false) - XCTAssertNoThrow(try embedded.writeInbound(goAwayFrame)) - XCTAssertEqual(delegate.goAwayReceived, true) - XCTAssertEqual(delegate.maxStreams, 10) - - while let streamID = openStreams.randomElement() { - openStreams.remove(streamID) - - let event = StreamClosedEvent(streamID: streamID, reason: nil) - XCTAssertTrue(embedded.isActive) - embedded.pipeline.fireUserInboundEventTriggered(event) - if openStreams.isEmpty { - XCTAssertFalse(embedded.isActive) - } else { - XCTAssertTrue(embedded.isActive) - } - } - } - - func testReceiveSettingsAndGoAwayAfterClientSideClose() { - let delegate = MockHTTP2IdleHandlerDelegate() - let idleHandler = HTTP2IdleHandler(delegate: delegate, logger: Logger(label: "test")) - let embedded = EmbeddedChannel(handlers: [idleHandler]) - XCTAssertNoThrow(try embedded.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait()) - - let settingsFrame = HTTP2Frame( - streamID: 0, - payload: .settings(.settings([.init(parameter: .maxConcurrentStreams, value: 10)])) - ) - XCTAssertEqual(delegate.maxStreams, nil) - XCTAssertNoThrow(try embedded.writeInbound(settingsFrame)) - XCTAssertEqual(delegate.maxStreams, 10) - - XCTAssertTrue(embedded.isActive) - embedded.pipeline.triggerUserOutboundEvent(HTTPConnectionEvent.shutdownRequested, promise: nil) - XCTAssertFalse(embedded.isActive) - - let newSettingsFrame = HTTP2Frame( - streamID: 0, - payload: .settings(.settings([.init(parameter: .maxConcurrentStreams, value: 20)])) - ) - XCTAssertEqual(delegate.maxStreams, 10) - XCTAssertNoThrow(try embedded.writeInbound(newSettingsFrame)) - XCTAssertEqual(delegate.maxStreams, 10, "Expected message to not be forwarded.") - - let goAwayFrame = HTTP2Frame( - streamID: HTTP2StreamID(0), - payload: .goAway(lastStreamID: 2, errorCode: .http11Required, opaqueData: nil) - ) - XCTAssertEqual(delegate.goAwayReceived, false) - XCTAssertNoThrow(try embedded.writeInbound(goAwayFrame)) - XCTAssertEqual(delegate.goAwayReceived, false, "Expected go away to not be forwarded.") - } - - func testConnectionUseLimitTriggersGoAway() { - let delegate = MockHTTP2IdleHandlerDelegate() - let idleHandler = HTTP2IdleHandler(delegate: delegate, logger: Logger(label: "test"), maximumConnectionUses: 5) - let embedded = EmbeddedChannel(handlers: [idleHandler]) - XCTAssertNoThrow(try embedded.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait()) - - let settingsFrame = HTTP2Frame( - streamID: 0, - payload: .settings(.settings([.init(parameter: .maxConcurrentStreams, value: 100)])) - ) - XCTAssertEqual(delegate.maxStreams, nil) - XCTAssertNoThrow(try embedded.writeInbound(settingsFrame)) - XCTAssertEqual(delegate.maxStreams, 100) - - for streamID in HTTP2StreamID(1)..! - var defaultClient: HTTPClient! - var backgroundLogStore: CollectEverythingLogHandler.LogStore! - - var defaultHTTPBinURLPrefix: String { - "http://localhost/(self.defaultHTTPBin.port)/" - } - - override func setUp() { - XCTAssertNil(self.clientGroup) - XCTAssertNil(self.serverGroup) - XCTAssertNil(self.defaultHTTPBin) - XCTAssertNil(self.defaultClient) - XCTAssertNil(self.backgroundLogStore) - - self.clientGroup = getDefaultEventLoopGroup(numberOfThreads: 1) - self.serverGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - self.defaultHTTPBin = HTTPBin() - self.backgroundLogStore = CollectEverythingLogHandler.LogStore() - var backgroundLogger = Logger( - label: "\(#function)", - factory: { _ in - CollectEverythingLogHandler(logStore: self.backgroundLogStore!) - } - ) - backgroundLogger.logLevel = .trace - self.defaultClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - backgroundActivityLogger: backgroundLogger - ) - } - - override func tearDown() { - if let defaultClient = self.defaultClient { - XCTAssertNoThrow(try defaultClient.syncShutdown()) - self.defaultClient = nil - } - - XCTAssertNotNil(self.defaultHTTPBin) - XCTAssertNoThrow(try self.defaultHTTPBin.shutdown()) - self.defaultHTTPBin = nil - - XCTAssertNotNil(self.clientGroup) - XCTAssertNoThrow(try self.clientGroup.syncShutdownGracefully()) - self.clientGroup = nil - - XCTAssertNotNil(self.serverGroup) - XCTAssertNoThrow(try self.serverGroup.syncShutdownGracefully()) - self.serverGroup = nil - - XCTAssertNotNil(self.backgroundLogStore) - self.backgroundLogStore = nil - } - - func testProxySOCKS() throws { - let socksBin = try MockSOCKSServer(expectedURL: "/socks/test", expectedResponse: "it works!") - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: .init( - proxy: .socksServer(host: "localhost", port: socksBin.port) - ).enableFastFailureModeForTesting() - ) - - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try socksBin.shutdown()) - } - - var response: HTTPClient.Response? - XCTAssertNoThrow(response = try localClient.get(url: "http://localhost/socks/test").wait()) - XCTAssertEqual(.ok, response?.status) - XCTAssertEqual(ByteBuffer(string: "it works!"), response?.body) - } - - func testProxySOCKSBogusAddress() throws { - let config = HTTPClient.Configuration(proxy: .socksServer(host: "127.0..")) - .enableFastFailureModeForTesting() - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: config - ) - - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - } - XCTAssertThrowsError(try localClient.get(url: "http://localhost/socks/test").wait()) - } - - // there is no socks server, so we should fail - func testProxySOCKSFailureNoServer() throws { - let localHTTPBin = HTTPBin() - let config = HTTPClient.Configuration(proxy: .socksServer(host: "localhost", port: localHTTPBin.port)) - .enableFastFailureModeForTesting() - - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: config - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - XCTAssertThrowsError(try localClient.get(url: "http://localhost/socks/test").wait()) - } - - // speak to a server that doesn't speak SOCKS - func testProxySOCKSFailureInvalidServer() throws { - let config = HTTPClient.Configuration(proxy: .socksServer(host: "localhost")) - .enableFastFailureModeForTesting() - - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: config - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - } - XCTAssertThrowsError(try localClient.get(url: "http://localhost/socks/test").wait()) - } - - // test a handshake failure with a misbehaving server - func testProxySOCKSMisbehavingServer() throws { - let socksBin = try MockSOCKSServer(expectedURL: "/socks/test", expectedResponse: "it works!", misbehave: true) - let config = HTTPClient.Configuration(proxy: .socksServer(host: "localhost", port: socksBin.port)) - .enableFastFailureModeForTesting() - - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: config - ) - - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try socksBin.shutdown()) - } - - // the server will send a bogus message in response to the clients greeting - // this will be first picked up as an invalid protocol - XCTAssertThrowsError(try localClient.get(url: "http://localhost/socks/test").wait()) { e in - XCTAssertTrue(e is SOCKSError.InvalidProtocolVersion) - } - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTPClient+StructuredConcurrencyTests.swift b/Tests/AsyncHTTPClientTests/HTTPClient+StructuredConcurrencyTests.swift deleted file mode 100644 index a7cc1f454..000000000 --- a/Tests/AsyncHTTPClientTests/HTTPClient+StructuredConcurrencyTests.swift +++ /dev/null @@ -1,101 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2025 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import AsyncHTTPClient -import NIO -import NIOFoundationCompat -import NIOHTTP1 -import XCTest - -final class HTTPClientStructuredConcurrencyTests: XCTestCase { - func testDoNothingWorks() async throws { - let actual = try await HTTPClient.withHTTPClient { httpClient in - "OK" - } - XCTAssertEqual("OK", actual) - } - - func testShuttingDownTheClientInBodyLeadsToError() async { - do { - let actual = try await HTTPClient.withHTTPClient { httpClient in - try await httpClient.shutdown() - return "OK" - } - XCTFail("Expected error, got \(actual)") - } catch let error as HTTPClientError where error == .alreadyShutdown { - // OK - } catch { - XCTFail("unexpected error: \(error)") - } - } - - func testBasicRequest() async throws { - let httpBin = HTTPBin() - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let actualBytes = try await HTTPClient.withHTTPClient { httpClient in - let response = try await httpClient.get(url: httpBin.baseURL).get() - XCTAssertEqual(response.status, .ok) - return response.body ?? ByteBuffer(string: "n/a") - } - let actual = try JSONDecoder().decode(RequestInfo.self, from: actualBytes) - - XCTAssertGreaterThanOrEqual(actual.requestNumber, 0) - XCTAssertGreaterThanOrEqual(actual.connectionNumber, 0) - } - - func testClientIsShutDownAfterReturn() async throws { - let leakedClient = try await HTTPClient.withHTTPClient { httpClient in - httpClient - } - do { - try await leakedClient.shutdown() - XCTFail("unexpected, shutdown should have failed") - } catch let error as HTTPClientError where error == .alreadyShutdown { - // OK - } catch { - XCTFail("unexpected error: \(error)") - } - } - - func testClientIsShutDownOnThrowAlso() async throws { - struct TestError: Error { - var httpClient: HTTPClient - } - - let leakedClient: HTTPClient - do { - try await HTTPClient.withHTTPClient { httpClient in - throw TestError(httpClient: httpClient) - } - XCTFail("unexpected, shutdown should have failed") - return - } catch let error as TestError { - // OK - leakedClient = error.httpClient - } catch { - XCTFail("unexpected error: \(error)") - return - } - - do { - try await leakedClient.shutdown() - XCTFail("unexpected, shutdown should have failed") - } catch let error as HTTPClientError where error == .alreadyShutdown { - // OK - } catch { - XCTFail("unexpected error: \(error)") - } - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTPClientBase.swift b/Tests/AsyncHTTPClientTests/HTTPClientBase.swift deleted file mode 100644 index aaf072b2f..000000000 --- a/Tests/AsyncHTTPClientTests/HTTPClientBase.swift +++ /dev/null @@ -1,92 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2022 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import AsyncHTTPClient -import Atomics -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOFoundationCompat -import NIOHTTP1 -import NIOHTTPCompression -import NIOPosix -import NIOSSL -import NIOTestUtils -import NIOTransportServices -import XCTest - -#if canImport(Network) -import Network -#endif - -class XCTestCaseHTTPClientTestsBaseClass: XCTestCase { - typealias Request = HTTPClient.Request - - var clientGroup: EventLoopGroup! - var serverGroup: EventLoopGroup! - var defaultHTTPBin: HTTPBin! - var defaultClient: HTTPClient! - var backgroundLogStore: CollectEverythingLogHandler.LogStore! - - var defaultHTTPBinURLPrefix: String { - self.defaultHTTPBin.baseURL - } - - override func setUp() { - XCTAssertNil(self.clientGroup) - XCTAssertNil(self.serverGroup) - XCTAssertNil(self.defaultHTTPBin) - XCTAssertNil(self.defaultClient) - XCTAssertNil(self.backgroundLogStore) - - self.clientGroup = getDefaultEventLoopGroup(numberOfThreads: 1) - self.serverGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - self.defaultHTTPBin = HTTPBin() - self.backgroundLogStore = CollectEverythingLogHandler.LogStore() - var backgroundLogger = Logger( - label: "\(#function)", - factory: { _ in - CollectEverythingLogHandler(logStore: self.backgroundLogStore!) - } - ) - backgroundLogger.logLevel = .trace - self.defaultClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: HTTPClient.Configuration().enableFastFailureModeForTesting(), - backgroundActivityLogger: backgroundLogger - ) - } - - override func tearDown() { - if let defaultClient = self.defaultClient { - XCTAssertNoThrow(try defaultClient.syncShutdown()) - self.defaultClient = nil - } - - XCTAssertNotNil(self.defaultHTTPBin) - XCTAssertNoThrow(try self.defaultHTTPBin.shutdown()) - self.defaultHTTPBin = nil - - XCTAssertNotNil(self.clientGroup) - XCTAssertNoThrow(try self.clientGroup.syncShutdownGracefully()) - self.clientGroup = nil - - XCTAssertNotNil(self.serverGroup) - XCTAssertNoThrow(try self.serverGroup.syncShutdownGracefully()) - self.serverGroup = nil - - XCTAssertNotNil(self.backgroundLogStore) - self.backgroundLogStore = nil - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTPClientCookieTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientCookieTests.swift deleted file mode 100644 index fa9abb9d8..000000000 --- a/Tests/AsyncHTTPClientTests/HTTPClientCookieTests.swift +++ /dev/null @@ -1,503 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2018-2019 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import AsyncHTTPClient -import CAsyncHTTPClient -import Foundation -import XCTest - -class HTTPClientCookieTests: XCTestCase { - func testCookie() { - let v = - "key=value; PaTh=/path; DoMaIn=EXampLE.CoM; eXpIRes=Wed, 21 Oct 2015 07:28:00 GMT; max-AGE=42; seCURE; HTTPOnly" - guard let c = HTTPClient.Cookie(header: v, defaultDomain: "exAMPle.cOm") else { - XCTFail("Failed to parse cookie") - return - } - XCTAssertEqual("key", c.name) - XCTAssertEqual("value", c.value) - XCTAssertEqual("/path", c.path) - XCTAssertEqual("example.com", c.domain) - XCTAssertEqual(Date(timeIntervalSince1970: 1_445_412_480), c.expires) - XCTAssertEqual(42, c.maxAge) - XCTAssertTrue(c.httpOnly) - XCTAssertTrue(c.secure) - } - - func testEmptyValueCookie() { - let v = "cookieValue=; Path=/" - guard let c = HTTPClient.Cookie(header: v, defaultDomain: "example.com") else { - XCTFail("Failed to parse cookie") - return - } - XCTAssertEqual("cookieValue", c.name) - XCTAssertEqual("", c.value) - XCTAssertEqual("/", c.path) - XCTAssertEqual("example.com", c.domain) - XCTAssertNil(c.expires) - XCTAssertNil(c.maxAge) - XCTAssertFalse(c.httpOnly) - XCTAssertFalse(c.secure) - } - - func testCookieDefaults() { - let v = "key=value" - guard let c = HTTPClient.Cookie(header: v, defaultDomain: "exAMPle.com") else { - XCTFail("Failed to parse cookie") - return - } - XCTAssertEqual("key", c.name) - XCTAssertEqual("value", c.value) - XCTAssertEqual("/", c.path) - XCTAssertEqual("example.com", c.domain) - XCTAssertNil(c.expires) - XCTAssertNil(c.maxAge) - XCTAssertFalse(c.httpOnly) - XCTAssertFalse(c.secure) - } - - func testCookieInit() { - let c = HTTPClient.Cookie( - name: "key", - value: "value", - path: "/path", - domain: "example.com", - expires: Date(timeIntervalSince1970: 1_445_412_480), - maxAge: 42, - httpOnly: true, - secure: true - ) - XCTAssertEqual("key", c.name) - XCTAssertEqual("value", c.value) - XCTAssertEqual("/path", c.path) - XCTAssertEqual("example.com", c.domain) - XCTAssertEqual(Date(timeIntervalSince1970: 1_445_412_480), c.expires) - XCTAssertEqual(42, c.maxAge) - XCTAssertTrue(c.httpOnly) - XCTAssertTrue(c.secure) - } - - func testMalformedCookies() { - XCTAssertNil(HTTPClient.Cookie(header: "", defaultDomain: "exampe.org")) - XCTAssertNil(HTTPClient.Cookie(header: "name", defaultDomain: "exampe.org")) - XCTAssertNil(HTTPClient.Cookie(header: ";;", defaultDomain: "exampe.org")) - XCTAssertNil(HTTPClient.Cookie(header: "name;;", defaultDomain: "exampe.org")) - XCTAssertNotNil(HTTPClient.Cookie(header: "name=;;", defaultDomain: "exampe.org")) - XCTAssertNotNil(HTTPClient.Cookie(header: "name=value;;", defaultDomain: "exampe.org")) - XCTAssertNotNil(HTTPClient.Cookie(header: "name=value;x;", defaultDomain: "exampe.org")) - XCTAssertNotNil(HTTPClient.Cookie(header: "name=value;x=;", defaultDomain: "exampe.org")) - XCTAssertNotNil(HTTPClient.Cookie(header: "name=value;;x=;", defaultDomain: "exampe.org")) - XCTAssertNil(HTTPClient.Cookie(header: ";key=value", defaultDomain: "exampe.org")) - XCTAssertNil(HTTPClient.Cookie(header: "key;key=value", defaultDomain: "exampe.org")) - XCTAssertNil(HTTPClient.Cookie(header: "=;", defaultDomain: "exampe.org")) - XCTAssertNil(HTTPClient.Cookie(header: "=value;", defaultDomain: "exampe.org")) - } - - func testExpires() { - // Empty values, and unrecognized timestamps, are ignored. - // https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.1 - var c = HTTPClient.Cookie(header: "key=value; expires=", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertNil(c?.expires) - - c = HTTPClient.Cookie(header: "key=value; expires", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertNil(c?.expires) - - c = HTTPClient.Cookie(header: "key=value; expires=foo", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertNil(c?.expires) - - c = HTTPClient.Cookie(header: "key=value; expires=04/01/2022", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertNil(c?.expires) - - // Later values override earlier values, except if they are ignored. - c = HTTPClient.Cookie( - header: "key=value; expires=Sunday, 06-Nov-94 08:49:37 GMT; expires=04/01/2022", - defaultDomain: "example.com" - ) - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual(Date(timeIntervalSince1970: 784_111_777), c?.expires) - - c = HTTPClient.Cookie( - header: "key=value; expires=Sunday, 06-Nov-94 08:49:37 GMT; expires=", - defaultDomain: "example.com" - ) - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual(Date(timeIntervalSince1970: 784_111_777), c?.expires) - - c = HTTPClient.Cookie( - header: "key=value; expires=Sunday, 06-Nov-94 08:49:37 GMT; expires", - defaultDomain: "example.com" - ) - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual(Date(timeIntervalSince1970: 784_111_777), c?.expires) - - // For more comprehensive tests of the various timestamp formats, see: `testCookieExpiresDateParsing`. - } - - func testMaxAge() { - // Empty values, and values containing non-digits, are ignored. - // https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.2 - var c = HTTPClient.Cookie(header: "key=value; max-age=", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertNil(c?.maxAge) - - c = HTTPClient.Cookie(header: "key=value; max-age", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertNil(c?.maxAge) - - c = HTTPClient.Cookie(header: "key=value; max-age=foo", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertNil(c?.maxAge) - - c = HTTPClient.Cookie(header: "key=value; max-age=123foo", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertNil(c?.maxAge) - - // Later values override earlier values, except if they are ignored. - c = HTTPClient.Cookie(header: "key=value; max-age=123; max-age=456baz", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual(123, c?.maxAge) - - c = HTTPClient.Cookie(header: "key=value; max-age=-123; max-age=", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual(-123, c?.maxAge) - - c = HTTPClient.Cookie(header: "key=value; max-age=123; max-age", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual(123, c?.maxAge) - } - - func testDomain() { - // Empty domains should be ignored. - // https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.3 - var c = HTTPClient.Cookie(header: "key=value; domain=", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual("example.com", c?.domain) - - c = HTTPClient.Cookie(header: "key=value; domain", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual("example.com", c?.domain) - - // A single leading dot is stripped. - c = HTTPClient.Cookie(header: "key=value; domain=.foo", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual("foo", c?.domain) - - c = HTTPClient.Cookie(header: "key=value; domain=..foo", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual(".foo", c?.domain) - - // RFC-6562 checks for empty values before stipping the dot (resulting in an empty domain), - // but later, empty domains are placed by the canonicalized request host. - // We use the default domain as the request host. - c = HTTPClient.Cookie(header: "key=value; domain=.", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual("example.com", c?.domain) - - // Later values override earlier values, except if they are ignored. - c = HTTPClient.Cookie(header: "key=value; domain=foo; domain=bar", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual("bar", c?.domain) - - c = HTTPClient.Cookie(header: "key=value; domain=foo; domain=", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual("foo", c?.domain) - - c = HTTPClient.Cookie(header: "key=value; domain=foo; domain", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual("foo", c?.domain) - - c = HTTPClient.Cookie(header: "key=value; domain=foo; domain=.", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual("example.com", c?.domain) - - // The domain (including the defaultDomain parameter) should be normalized to lowercase. - c = HTTPClient.Cookie(header: "key=value; domain=FOO; domain", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual("foo", c?.domain) - - c = HTTPClient.Cookie(header: "key=value; domain=; domain", defaultDomain: "EXAMPLE.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual("example.com", c?.domain) - } - - func testPath() { - // An empty path, or path which does not begin with a "/", is considered the default path. - // https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.4 - var c = HTTPClient.Cookie(header: "key=value; path=", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual("/", c?.path) - - c = HTTPClient.Cookie(header: "key=value; path", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual("/", c?.path) - - c = HTTPClient.Cookie(header: "key=value; path=foo", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual("/", c?.path) - - // Later path values override earlier values, even if the later value is considered the default path. - c = HTTPClient.Cookie(header: "key=value; path=/abc; path=/foo", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual("/foo", c?.path) - - c = HTTPClient.Cookie(header: "key=value; path=/abc; path=foo", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual("/", c?.path) - - c = HTTPClient.Cookie(header: "key=value; path=/abc; path", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual("/", c?.path) - } - - func testSecure() { - // If the cookie contains a key called "secure" (case-insensitive), the secure flag is set. - // Regardless of its value. - // https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.5 - var c = HTTPClient.Cookie(header: "key=value; secure=", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual(true, c?.secure) - - c = HTTPClient.Cookie(header: "key=value; secure", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual(true, c?.secure) - - c = HTTPClient.Cookie(header: "key=value; secure=0", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual(true, c?.secure) - - c = HTTPClient.Cookie(header: "key=value; secure=false", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual(true, c?.secure) - - c = HTTPClient.Cookie(header: "key=value; secure=no", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual(true, c?.secure) - } - - func testHttpOnly() { - // If the cookie contains a key called "httponly" (case-insensitive), the http-only flag is set. - // Regardless of its value. - // https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.6 - var c = HTTPClient.Cookie(header: "key=value; httponly=", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual(true, c?.httpOnly) - - c = HTTPClient.Cookie(header: "key=value; httponly", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual(true, c?.httpOnly) - - c = HTTPClient.Cookie(header: "key=value; httponly=0", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual(true, c?.httpOnly) - - c = HTTPClient.Cookie(header: "key=value; httponly=false", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual(true, c?.httpOnly) - - c = HTTPClient.Cookie(header: "key=value; httponly=no", defaultDomain: "example.com") - XCTAssertEqual("key", c?.name) - XCTAssertEqual("value", c?.value) - XCTAssertEqual(true, c?.httpOnly) - } - - func testCookieExpiresDateParsing() { - let domain = "example.org" - - // Regular formats. - var c = HTTPClient.Cookie(header: "key=value; eXpIRes=Sun, 06 Nov 1994 08:49:37 GMT;", defaultDomain: domain) - XCTAssertEqual(Date(timeIntervalSince1970: 784_111_777), c?.expires) - c = HTTPClient.Cookie(header: "key=value; eXpIRes=Sunday, 06-Nov-94 08:49:37 GMT;", defaultDomain: domain) - XCTAssertEqual(Date(timeIntervalSince1970: 784_111_777), c?.expires) - c = HTTPClient.Cookie(header: "key=value; eXpIRes=Sun Nov 6 08:49:37 1994;", defaultDomain: domain) - XCTAssertEqual(Date(timeIntervalSince1970: 784_111_777), c?.expires) - - // GMT is implicit. - // Formats which typically include it may omit it; formats which typically omit it may include it. - c = HTTPClient.Cookie(header: "key=value; expires=Sun, 06 Nov 1994 08:49:37;", defaultDomain: domain) - XCTAssertEqual(Date(timeIntervalSince1970: 784_111_777), c?.expires) - c = HTTPClient.Cookie(header: "key=value; expires=Sunday, 06-Nov-94 08:49:37;", defaultDomain: domain) - XCTAssertEqual(Date(timeIntervalSince1970: 784_111_777), c?.expires) - c = HTTPClient.Cookie(header: "key=value; expires=Sun Nov 6 08:49:37 1994 GMT;", defaultDomain: domain) - XCTAssertEqual(Date(timeIntervalSince1970: 784_111_777), c?.expires) - - // If GMT is explicit, it must be separated from the timestamp by at least one space. - c = HTTPClient.Cookie(header: "key=value; expires=Sun, 06 Nov 1994 08:49:37GMT;", defaultDomain: domain) - XCTAssertNil(c?.expires) - c = HTTPClient.Cookie(header: "key=value; expires=Sunday, 06-Nov-94 08:49:37GMT;", defaultDomain: domain) - XCTAssertNil(c?.expires) - c = HTTPClient.Cookie(header: "key=value; expires=Sun Nov 6 08:49:37 1994GMT;", defaultDomain: domain) - XCTAssertNil(c?.expires) - - // Where space are required, any number of spaces are okay. - c = HTTPClient.Cookie(header: "key=value; expires=Sun, 06 Nov 1994 08:49:37;", defaultDomain: domain) - XCTAssertEqual(Date(timeIntervalSince1970: 784_111_777), c?.expires) - c = HTTPClient.Cookie(header: "key=value; expires=Sunday, 06-Nov-94 08:49:37;", defaultDomain: domain) - XCTAssertEqual(Date(timeIntervalSince1970: 784_111_777), c?.expires) - c = HTTPClient.Cookie(header: "key=value; expires= Sun Nov 6 08:49:37 1994;", defaultDomain: domain) - XCTAssertEqual(Date(timeIntervalSince1970: 784_111_777), c?.expires) - c = HTTPClient.Cookie(header: "key=value; expires=Sun, 06 Nov 1994 08:49:37 GMT;", defaultDomain: domain) - XCTAssertEqual(Date(timeIntervalSince1970: 784_111_777), c?.expires) - c = HTTPClient.Cookie(header: "key=value; expires=Sunday, 06-Nov-94 08:49:37 GMT;", defaultDomain: domain) - XCTAssertEqual(Date(timeIntervalSince1970: 784_111_777), c?.expires) - c = HTTPClient.Cookie(header: "key=value; expires=Sun Nov 6 08:49:37 1994 GMT;", defaultDomain: domain) - XCTAssertEqual(Date(timeIntervalSince1970: 784_111_777), c?.expires) - - // Where spaces are required, tabs and newlines are not okay. - c = HTTPClient.Cookie(header: "key=value; expires=Sun,\t06 Nov 1994 08:49:37 GMT;", defaultDomain: domain) - XCTAssertNil(c?.expires) - c = HTTPClient.Cookie(header: "key=value; expires=Sun,\n06 Nov 1994 08:49:37 GMT;", defaultDomain: domain) - XCTAssertNil(c?.expires) - c = HTTPClient.Cookie(header: "key=value; expires=Sun, 06 Nov 1994 08:49:37\tGMT;", defaultDomain: domain) - XCTAssertNil(c?.expires) - c = HTTPClient.Cookie(header: "key=value; expires=Sun, 06 Nov 1994 08:49:37\nGMT;", defaultDomain: domain) - XCTAssertNil(c?.expires) - - // Spaces are only allowed in particular locations. - c = HTTPClient.Cookie(header: "key=value; expires=Sunday, 06- Nov-94 08:49:37;", defaultDomain: domain) - XCTAssertNil(c?.expires) - c = HTTPClient.Cookie(header: "key=value; expires= Sun Nov 6 08:4 9:37 1994;", defaultDomain: domain) - XCTAssertNil(c?.expires) - - // Incorrect comma placement. - c = HTTPClient.Cookie(header: "key=value; expires=Sun 06 Nov 1994 08:49:37 GMT;", defaultDomain: domain) - XCTAssertNil(c?.expires) - c = HTTPClient.Cookie(header: "key=value; expires=Sunday 06-Nov-94 08:49:37 GMT;", defaultDomain: domain) - XCTAssertNil(c?.expires) - c = HTTPClient.Cookie(header: "key=value; expires=Sun, Nov 6 08:49:37 1994 GMT;", defaultDomain: domain) - XCTAssertNil(c?.expires) - - // Incorrect delimiters. - c = HTTPClient.Cookie(header: "key=value; expires=Sunday 06/Nov/94 08:49:37 GMT;", defaultDomain: domain) - XCTAssertNil(c?.expires) - c = HTTPClient.Cookie(header: "key=value; expires=Sun, Nov 6 08-49-37 1994 GMT;", defaultDomain: domain) - XCTAssertNil(c?.expires) - - // Non-GMT timezones are rejected. - c = HTTPClient.Cookie(header: "key=value; expires=Sun, 06 Nov 1994 08:49:37 BST;", defaultDomain: domain) - XCTAssertNil(c?.expires) - c = HTTPClient.Cookie(header: "key=value; expires=Sunday, 06-Nov-94 08:49:37 PST;", defaultDomain: domain) - XCTAssertNil(c?.expires) - c = HTTPClient.Cookie(header: "key=value; expires=Sun Nov 6 08:49:37 1994 CET;", defaultDomain: domain) - XCTAssertNil(c?.expires) - - c = HTTPClient.Cookie(header: "key=value; expires=GMT;", defaultDomain: domain) - XCTAssertNil(c?.expires) - c = HTTPClient.Cookie(header: "key=value; expires=\" GMT\";", defaultDomain: domain) - XCTAssertNil(c?.expires) - c = HTTPClient.Cookie(header: "key=value; expires=CET;", defaultDomain: domain) - XCTAssertNil(c?.expires) - } - - func testQuotedCookies() { - var c = HTTPClient.Cookie(header: "key=\"value\"", defaultDomain: "example.org") - XCTAssertEqual("value", c?.value) - - c = HTTPClient.Cookie(header: "key=\"value\"; Path=/path", defaultDomain: "example.org") - XCTAssertEqual("value", c?.value) - XCTAssertEqual("/path", c?.path) - - c = HTTPClient.Cookie(header: "key=\"\"", defaultDomain: "example.org") - XCTAssertEqual("", c?.value) - - // Spaces inside paired quotes are not trimmed. - c = HTTPClient.Cookie(header: "key=\" abc \"", defaultDomain: "example.org") - XCTAssertEqual(" abc ", c?.value) - - c = HTTPClient.Cookie(header: "key=\" \"", defaultDomain: "example.org") - XCTAssertEqual(" ", c?.value) - - // Unpaired quote at start of value. - c = HTTPClient.Cookie(header: "key=\"abc", defaultDomain: "example.org") - XCTAssertEqual("\"abc", c?.value) - - // Unpaired quote in the middle of the value. - c = HTTPClient.Cookie(header: "key=ab\"c", defaultDomain: "example.org") - XCTAssertEqual("ab\"c", c?.value) - - // Unpaired quote at the end of the value. - c = HTTPClient.Cookie(header: "key=abc\"", defaultDomain: "example.org") - XCTAssertEqual("abc\"", c?.value) - } - - func testCookieExpiresDateParsingWithNonEnglishLocale() throws { - try withCLocaleSetToGerman { - // Check that we are using a German C locale. - var localeCheck = tm() - guard swiftahc_cshims_strptime("Freitag Februar", "%a %b", &localeCheck) else { - throw XCTSkip("Unable to set locale") - } - // These values are zero-based 🙄 - try XCTSkipIf(localeCheck.tm_wday != 5, "Unable to set locale") - try XCTSkipIf(localeCheck.tm_mon != 1, "Unable to set locale") - - // Cookie parsing should be independent of C locale. - var c = HTTPClient.Cookie( - header: "key=value; eXpIRes=Sunday, 06-Nov-94 08:49:37 GMT;", - defaultDomain: "example.org" - ) - XCTAssertEqual(Date(timeIntervalSince1970: 784_111_777), c?.expires) - c = HTTPClient.Cookie(header: "key=value; eXpIRes=Sun Nov 6 08:49:37 1994;", defaultDomain: "example.org")! - XCTAssertEqual(Date(timeIntervalSince1970: 784_111_777), c?.expires) - c = HTTPClient.Cookie( - header: "key=value; eXpIRes=Sonntag, 06-Nov-94 08:49:37 GMT;", - defaultDomain: "example.org" - )! - XCTAssertNil(c?.expires) - } - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTPClientInformationalResponsesTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientInformationalResponsesTests.swift deleted file mode 100644 index 5c41a6adb..000000000 --- a/Tests/AsyncHTTPClientTests/HTTPClientInformationalResponsesTests.swift +++ /dev/null @@ -1,133 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import AsyncHTTPClient -import Logging -import NIOCore -import NIOHTTP1 -import XCTest - -final class HTTPClientReproTests: XCTestCase { - func testServerSends100ContinueFirst() { - final class HTTPInformationalResponseHandler: ChannelInboundHandler { - typealias InboundIn = HTTPServerRequestPart - typealias OutboundOut = HTTPServerResponsePart - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - switch self.unwrapInboundIn(data) { - case .head: - context.writeAndFlush( - self.wrapOutboundOut(.head(.init(version: .http1_1, status: .continue))), - promise: nil - ) - case .body: - break - case .end: - context.write(self.wrapOutboundOut(.head(.init(version: .http1_1, status: .ok))), promise: nil) - context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: nil) - } - } - } - - let client = HTTPClient(eventLoopGroupProvider: .singleton) - defer { XCTAssertNoThrow(try client.syncShutdown()) } - - let httpBin = HTTPBin(.http1_1(ssl: false, compress: false)) { _ in - HTTPInformationalResponseHandler() - } - - let body = #"{"foo": "bar"}"# - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow( - maybeRequest = try HTTPClient.Request( - url: "http://localhost/(httpBin.port)/", - method: .POST, - headers: [ - "Content-Type": "application/json" - ], - body: .string(body) - ) - ) - guard let request = maybeRequest else { return XCTFail("Expected to have a request here") } - - var logger = Logger(label: "test") - logger.logLevel = .trace - - var response: HTTPClient.Response? - XCTAssertNoThrow(response = try client.execute(request: request, logger: logger).wait()) - XCTAssertEqual(response?.status, .ok) - } - - func testServerSendsSwitchingProtocols() { - final class HTTPInformationalResponseHandler: ChannelInboundHandler { - typealias InboundIn = HTTPServerRequestPart - typealias OutboundOut = HTTPServerResponsePart - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - switch self.unwrapInboundIn(data) { - case .head: - let head = HTTPResponseHead( - version: .http1_1, - status: .switchingProtocols, - headers: [ - "Connection": "Upgrade", - "Upgrade": "Websocket", - ] - ) - let body = context.channel.allocator.buffer(string: "foo bar") - - context.write(self.wrapOutboundOut(.head(head)), promise: nil) - context.write(self.wrapOutboundOut(.body(.byteBuffer(body))), promise: nil) - // we purposefully don't send an `.end` here. - context.flush() - case .body: - break - case .end: - break - } - } - } - - let client = HTTPClient(eventLoopGroupProvider: .singleton) - defer { XCTAssertNoThrow(try client.syncShutdown()) } - - let httpBin = HTTPBin(.http1_1(ssl: false, compress: false)) { _ in - HTTPInformationalResponseHandler() - } - - let body = #"{"foo": "bar"}"# - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow( - maybeRequest = try HTTPClient.Request( - url: "http://localhost/(httpBin.port)/", - method: .POST, - headers: [ - "Content-Type": "application/json" - ], - body: .string(body) - ) - ) - guard let request = maybeRequest else { return XCTFail("Expected to have a request here") } - - var logger = Logger(label: "test") - logger.logLevel = .trace - - var response: HTTPClient.Response? - XCTAssertNoThrow(response = try client.execute(request: request, logger: logger).wait()) - XCTAssertEqual(response?.status, .switchingProtocols) - XCTAssertNil(response?.body) - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTPClientInternalTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientInternalTests.swift deleted file mode 100644 index 634efc14c..000000000 --- a/Tests/AsyncHTTPClientTests/HTTPClientInternalTests.swift +++ /dev/null @@ -1,694 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2018-2019 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOConcurrencyHelpers -import NIOCore -import NIOEmbedded -import NIOFoundationCompat -import NIOHTTP1 -import NIOPosix -import NIOTestUtils -import XCTest - -@testable import AsyncHTTPClient - -class HTTPClientInternalTests: XCTestCase { - typealias Request = HTTPClient.Request - typealias Task = HTTPClient.Task - - var serverGroup: EventLoopGroup! - var clientGroup: EventLoopGroup! - - override func setUp() { - XCTAssertNil(self.clientGroup) - XCTAssertNil(self.serverGroup) - self.serverGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - self.clientGroup = getDefaultEventLoopGroup(numberOfThreads: 1) - } - - override func tearDown() { - XCTAssertNotNil(self.serverGroup) - XCTAssertNoThrow(try self.serverGroup.syncShutdownGracefully()) - XCTAssertNotNil(self.clientGroup) - XCTAssertNoThrow(try self.clientGroup.syncShutdownGracefully()) - self.clientGroup = nil - self.serverGroup = nil - } - - func testProxyStreaming() throws { - let httpBin = HTTPBin() - let httpClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup)) - defer { - XCTAssertNoThrow(try httpClient.syncShutdown(requiresCleanClose: true)) - XCTAssertNoThrow(try httpBin.shutdown()) - } - - let body: HTTPClient.Body = .stream(contentLength: 50) { writer in - do { - var request = try Request(url: "http://localhost/(httpBin.port)/events/10/1") - request.headers.add(name: "Accept", value: "text/event-stream") - - let delegate = HTTPClientCopyingDelegate { part in - writer.write(.byteBuffer(part)) - } - return httpClient.execute(request: request, delegate: delegate).futureResult - } catch { - return httpClient.eventLoopGroup.next().makeFailedFuture(error) - } - } - - let upload = try! httpClient.post(url: "http://localhost/(httpBin.port)/post", body: body).wait() - let data = upload.body.flatMap { try? JSONDecoder().decode(RequestInfo.self, from: $0) } - - XCTAssertEqual(.ok, upload.status) - XCTAssertEqual("id: 0id: 1id: 2id: 3id: 4id: 5id: 6id: 7id: 8id: 9", data?.data) - } - - func testProxyStreamingFailure() throws { - let httpBin = HTTPBin() - let httpClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup)) - defer { - XCTAssertNoThrow(try httpClient.syncShutdown()) - XCTAssertNoThrow(try httpBin.shutdown()) - } - - var body: HTTPClient.Body = .stream(contentLength: 50) { _ in - httpClient.eventLoopGroup.next().makeFailedFuture(HTTPClientError.invalidProxyResponse) - } - - XCTAssertThrowsError(try httpClient.post(url: "http://localhost/(httpBin.port)/post", body: body).wait()) - - body = .stream(contentLength: 50) { _ in - do { - var request = try Request(url: "http://localhost/(httpBin.port)/events/10/1") - request.headers.add(name: "Accept", value: "text/event-stream") - - let delegate = HTTPClientCopyingDelegate { _ in - httpClient.eventLoopGroup.next().makeFailedFuture(HTTPClientError.invalidProxyResponse) - } - return httpClient.execute(request: request, delegate: delegate).futureResult - } catch { - return httpClient.eventLoopGroup.next().makeFailedFuture(error) - } - } - - XCTAssertThrowsError(try httpClient.post(url: "http://localhost/(httpBin.port)/post", body: body).wait()) - } - - func testRequestURITrailingSlash() throws { - let request1 = try Request(url: "https://someserver.com:8888/some/path?foo=bar#ref") - XCTAssertEqual(request1.url.uri, "/some/path?foo=bar") - - let request2 = try Request(url: "https://someserver.com:8888/some/path/?foo=bar#ref") - XCTAssertEqual(request2.url.uri, "/some/path/?foo=bar") - - let request3 = try Request(url: "https://someserver.com:8888/?foo=bar#ref") - XCTAssertEqual(request3.url.uri, "/?foo=bar") - - let request4 = try Request(url: "https://someserver.com:8888/?foo=bar#ref") - XCTAssertEqual(request4.url.uri, "/?foo=bar") - - let request5 = try Request(url: "https://someserver.com:8888/some/path") - XCTAssertEqual(request5.url.uri, "/some/path") - - let request6 = try Request(url: "https://someserver.com:8888/some/path/") - XCTAssertEqual(request6.url.uri, "/some/path/") - - let request7 = try Request(url: "https://someserver.com:8888/") - XCTAssertEqual(request7.url.uri, "/") - - let request8 = try Request(url: "https://someserver.com:8888/") - XCTAssertEqual(request8.url.uri, "/") - - let request9 = try Request(url: "https://someserver.com:8888/#ref") - XCTAssertEqual(request9.url.uri, "/") - - let request10 = try Request(url: "https://someserver.com:8888/#ref") - XCTAssertEqual(request10.url.uri, "/") - - let request11 = try Request(url: "https://someserver.com/some%20path") - XCTAssertEqual(request11.url.uri, "/some%20path") - - let request12 = try Request(url: "https://someserver.com/some%2Fpathsegment1/pathsegment2") - XCTAssertEqual(request12.url.uri, "/some%2Fpathsegment1/pathsegment2") - } - - func testURIOfRelativeURLRequest() throws { - let requestNoLeadingSlash = try Request( - url: URL( - string: "percent%2Fencoded/hello", - relativeTo: URL(string: "http://127.0.0.1/")! - )! - ) - - let requestWithLeadingSlash = try Request( - url: URL( - string: "/percent%2Fencoded/hello", - relativeTo: URL(string: "http://127.0.0.1/")! - )! - ) - - XCTAssertEqual(requestNoLeadingSlash.url.uri, "/percent%2Fencoded/hello") - XCTAssertEqual(requestWithLeadingSlash.url.uri, "/percent%2Fencoded/hello") - } - - func testChannelAndDelegateOnDifferentEventLoops() throws { - final class Delegate: HTTPClientResponseDelegate { - typealias Response = ([Message], [Message]) - - enum Message: Sendable { - case head(HTTPResponseHead) - case bodyPart(ByteBuffer) - case sentRequestHead(HTTPRequestHead) - case sentRequestPart(IOData) - case sentRequest - case error(Error) - } - - private struct Messages: Sendable { - var received: [Message] = [] - var sent: [Message] = [] - } - - private let messages: NIOLoopBoundBox - - var receivedMessages: [Message] { - get { - self.messages.value.received - } - set { - self.messages.value.received = newValue - } - } - var sentMessages: [Message] { - get { - self.messages.value.sent - } - set { - self.messages.value.sent = newValue - } - } - private let eventLoop: EventLoop - private let randoEL: EventLoop - - init(expectedEventLoop: EventLoop, randomOtherEventLoop: EventLoop) { - self.eventLoop = expectedEventLoop - self.randoEL = randomOtherEventLoop - self.messages = .makeBoxSendingValue(Messages(), eventLoop: expectedEventLoop) - } - - func didSendRequestHead(task: HTTPClient.Task, _ head: HTTPRequestHead) { - self.sentMessages.append(.sentRequestHead(head)) - } - - func didSendRequestPart(task: HTTPClient.Task, _ part: IOData) { - self.sentMessages.append(.sentRequestPart(part)) - } - - func didSendRequest(task: HTTPClient.Task) { - self.sentMessages.append(.sentRequest) - } - - func didReceiveError(task: HTTPClient.Task, _ error: Error) { - self.receivedMessages.append(.error(error)) - } - - public func didReceiveHead( - task: HTTPClient.Task, - _ head: HTTPResponseHead - ) -> EventLoopFuture { - self.receivedMessages.append(.head(head)) - return self.randoEL.makeSucceededFuture(()) - } - - func didReceiveBodyPart( - task: HTTPClient.Task, - _ buffer: ByteBuffer - ) -> EventLoopFuture { - self.receivedMessages.append(.bodyPart(buffer)) - return self.randoEL.makeSucceededFuture(()) - } - - func didFinishRequest(task: HTTPClient.Task) throws -> Response { - (self.receivedMessages, self.sentMessages) - } - } - - let group = getDefaultEventLoopGroup(numberOfThreads: 3) - let serverGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { - XCTAssertNoThrow(try group.syncShutdownGracefully()) - XCTAssertNoThrow(try serverGroup.syncShutdownGracefully()) - } - - let channelEL = group.next() - let delegateEL = group.next() - let randoEL = group.next() - - let httpClient = HTTPClient(eventLoopGroupProvider: .shared(group)) - let server = NIOHTTP1TestServer(group: serverGroup) - defer { - XCTAssertNoThrow(try server.stop()) - XCTAssertNoThrow(try httpClient.syncShutdown(requiresCleanClose: true)) - } - - let body: HTTPClient.Body = .stream(contentLength: 8) { writer in - let buffer = ByteBuffer(string: "1234") - return writer.write(.byteBuffer(buffer)).flatMap { - let buffer = ByteBuffer(string: "4321") - return writer.write(.byteBuffer(buffer)) - } - } - - let request = try Request( - url: "http://127.0.0.1/(server.serverPort)/custom", - body: body - ) - let delegate = Delegate(expectedEventLoop: delegateEL, randomOtherEventLoop: randoEL) - let future = httpClient.execute( - request: request, - delegate: delegate, - eventLoop: .init( - .testOnly_exact( - channelOn: channelEL, - delegateOn: delegateEL - ) - ) - ).futureResult - - XCTAssertNoThrow(try server.readInbound()) // .head - XCTAssertNoThrow(try server.readInbound()) // .body - XCTAssertNoThrow(try server.readInbound()) // .end - - // Send 3 parts, but only one should be received until the future is complete - XCTAssertNoThrow( - try server.writeOutbound( - .head( - .init( - version: .init(major: 1, minor: 1), - status: .ok, - headers: HTTPHeaders([("Transfer-Encoding", "chunked")]) - ) - ) - ) - ) - let buffer = ByteBuffer(string: "1234") - XCTAssertNoThrow(try server.writeOutbound(.body(.byteBuffer(buffer)))) - XCTAssertNoThrow(try server.writeOutbound(.end(nil))) - - let (receivedMessages, sentMessages) = try future.wait() - XCTAssertEqual(2, receivedMessages.count) - XCTAssertEqual(4, sentMessages.count) - - switch sentMessages.dropFirst(0).first { - case .some(.sentRequestHead(let head)): - XCTAssertEqual(request.url.uri, head.uri) - default: - XCTFail("wrong message") - } - - switch sentMessages.dropFirst(1).first { - case .some(.sentRequestPart(.byteBuffer(let buffer))): - XCTAssertEqual("1234", String(decoding: buffer.readableBytesView, as: Unicode.UTF8.self)) - default: - XCTFail("wrong message") - } - - switch sentMessages.dropFirst(2).first { - case .some(.sentRequestPart(.byteBuffer(let buffer))): - XCTAssertEqual("4321", String(decoding: buffer.readableBytesView, as: Unicode.UTF8.self)) - default: - XCTFail("wrong message") - } - - switch sentMessages.dropFirst(3).first { - case .some(.sentRequest): - () // OK - default: - XCTFail("wrong message") - } - - switch receivedMessages.dropFirst(0).first { - case .some(.head(let head)): - XCTAssertEqual(head.headers["transfer-encoding"].first, "chunked") - default: - XCTFail("wrong message") - } - - switch receivedMessages.dropFirst(1).first { - case .some(.bodyPart(let buffer)): - XCTAssertEqual("1234", String(decoding: buffer.readableBytesView, as: Unicode.UTF8.self)) - default: - XCTFail("wrong message") - } - } - - func testResponseFutureIsOnCorrectEL() throws { - let group = getDefaultEventLoopGroup(numberOfThreads: 4) - defer { - XCTAssertNoThrow(try group.syncShutdownGracefully()) - } - let client = HTTPClient(eventLoopGroupProvider: .shared(group)) - let httpBin = HTTPBin() - defer { - XCTAssertNoThrow(try client.syncShutdown()) - XCTAssertNoThrow(try httpBin.shutdown()) - } - - let request = try HTTPClient.Request(url: "http://localhost/(httpBin.port)/get") - var futures = [EventLoopFuture]() - for _ in 1...100 { - let el = group.next() - let req1 = client.execute(request: request, eventLoop: .delegate(on: el)) - let req2 = client.execute(request: request, eventLoop: .delegateAndChannel(on: el)) - let req3 = client.execute( - request: request, - eventLoop: .init(.testOnly_exact(channelOn: el, delegateOn: el)) - ) - XCTAssert(req1.eventLoop === el) - XCTAssert(req2.eventLoop === el) - XCTAssert(req3.eventLoop === el) - futures.append(contentsOf: [req1, req2, req3]) - } - try EventLoopFuture.andAllComplete(futures, on: group.next()).wait() - } - - func testUncleanCloseThrows() { - let server = NIOHTTP1TestServer(group: self.serverGroup) - defer { - XCTAssertNoThrow(try server.stop()) - } - - let httpClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup)) - - _ = httpClient.get(url: "http://localhost/(server.serverPort)/wait") - - XCTAssertNoThrow(try server.readInbound()) // .head - XCTAssertNoThrow(try server.readInbound()) // .end - - do { - try httpClient.syncShutdown(requiresCleanClose: true) - XCTFail("There should be an error on shutdown") - } catch { - guard let clientError = error as? HTTPClientError, clientError == .uncleanShutdown else { - XCTFail("Unexpected shutdown error: \(error)") - return - } - } - } - - func testUploadStreamingIsCalledOnTaskEL() throws { - let group = getDefaultEventLoopGroup(numberOfThreads: 4) - defer { - XCTAssertNoThrow(try group.syncShutdownGracefully()) - } - - let httpBin = HTTPBin() - let httpClient = HTTPClient(eventLoopGroupProvider: .shared(group)) - defer { - XCTAssertNoThrow(try httpClient.syncShutdown()) - XCTAssertNoThrow(try httpBin.shutdown()) - } - - let el1 = group.next() - let el2 = group.next() - XCTAssert(el1 !== el2) - - let body: HTTPClient.Body = .stream(contentLength: 8) { writer in - XCTAssert(el1.inEventLoop) - let buffer = ByteBuffer(string: "1234") - return writer.write(.byteBuffer(buffer)).flatMap { - XCTAssert(el1.inEventLoop) - let buffer = ByteBuffer(string: "4321") - return writer.write(.byteBuffer(buffer)) - } - } - let request = try HTTPClient.Request(url: "http://localhost/(httpBin.port)/post", method: .POST, body: body) - let response = httpClient.execute( - request: request, - delegate: ResponseAccumulator(request: request), - eventLoop: HTTPClient.EventLoopPreference( - .testOnly_exact( - channelOn: el2, - delegateOn: el1 - ) - ) - ) - XCTAssert(el1 === response.eventLoop) - XCTAssertNoThrow(try response.wait()) - } - - func testTaskPromiseBoundToEL() throws { - let elg = getDefaultEventLoopGroup(numberOfThreads: 2) - let el1 = elg.next() - let el2 = elg.next() - - let httpBin = HTTPBin() - let client = HTTPClient(eventLoopGroupProvider: .shared(elg)) - - defer { - XCTAssertNoThrow(try client.syncShutdown()) - XCTAssertNoThrow(try httpBin.shutdown()) - XCTAssertNoThrow(try elg.syncShutdownGracefully()) - } - - let request = try HTTPClient.Request(url: "http://localhost/(httpBin.port)//get") - let delegate = ResponseAccumulator(request: request) - let task = client.execute( - request: request, - delegate: delegate, - eventLoop: .init(.testOnly_exact(channelOn: el1, delegateOn: el2)) - ) - XCTAssertTrue(task.futureResult.eventLoop === el2) - XCTAssertNoThrow(try task.wait()) - } - - func testConnectErrorCalloutOnCorrectEL() throws { - final class TestDelegate: HTTPClientResponseDelegate { - typealias Response = Void - - let expectedEL: EventLoop - let _receivedError = NIOLockedValueBox(false) - - var receivedError: Bool { - self._receivedError.withLockedValue { $0 } - } - - init(expectedEL: EventLoop) { - self.expectedEL = expectedEL - } - - func didFinishRequest(task: HTTPClient.Task) throws {} - - func didReceiveError(task: HTTPClient.Task, _ error: Error) { - self._receivedError.withLockedValue { $0 = true } - XCTAssertTrue(self.expectedEL.inEventLoop) - } - } - - let elg = getDefaultEventLoopGroup(numberOfThreads: 2) - let el1 = elg.next() - let el2 = elg.next() - - let httpBin = HTTPBin(.refuse) - let config = HTTPClient.Configuration() - .enableFastFailureModeForTesting() - let client = HTTPClient(eventLoopGroupProvider: .shared(elg), configuration: config) - - defer { - XCTAssertNoThrow(try client.syncShutdown()) - XCTAssertNoThrow(try elg.syncShutdownGracefully()) - } - - let request = try HTTPClient.Request(url: "http://localhost/(httpBin.port)/get") - let delegate = TestDelegate(expectedEL: el1) - XCTAssertNoThrow(try httpBin.shutdown()) - let task = client.execute( - request: request, - delegate: delegate, - eventLoop: .init(.testOnly_exact(channelOn: el2, delegateOn: el1)) - ) - XCTAssertThrowsError(try task.wait()) - XCTAssertTrue(delegate.receivedError) - } - - func testInternalRequestURI() throws { - let request1 = try Request(url: "https://someserver.com:8888/some/path?foo=bar") - XCTAssertEqual(request1.deconstructedURL.scheme, .https) - XCTAssertEqual(request1.deconstructedURL.connectionTarget, .domain(name: "someserver.com", port: 8888)) - XCTAssertEqual(request1.deconstructedURL.uri, "/some/path?foo=bar") - - let request2 = try Request(url: "https://someserver.com/") - XCTAssertEqual(request2.deconstructedURL.scheme, .https) - XCTAssertEqual(request2.deconstructedURL.connectionTarget, .domain(name: "someserver.com", port: 443)) - XCTAssertEqual(request2.deconstructedURL.uri, "/") - - let request3 = try Request(url: "unix:///tmp/file") - XCTAssertEqual(request3.deconstructedURL.scheme, .unix) - XCTAssertEqual(request3.deconstructedURL.connectionTarget, .unixSocket(path: "/tmp/file")) - XCTAssertEqual(request3.deconstructedURL.uri, "/") - - let request4 = try Request(url: "http+unix://%2Ftmp%2Ffile/file/path") - XCTAssertEqual(request4.deconstructedURL.scheme, .httpUnix) - XCTAssertEqual(request4.deconstructedURL.connectionTarget, .unixSocket(path: "/tmp/file")) - XCTAssertEqual(request4.deconstructedURL.uri, "/file/path") - - let request5 = try Request(url: "https+unix://%2Ftmp%2Ffile/file/path") - XCTAssertEqual(request5.deconstructedURL.scheme, .httpsUnix) - XCTAssertEqual(request5.deconstructedURL.connectionTarget, .unixSocket(path: "/tmp/file")) - XCTAssertEqual(request5.deconstructedURL.uri, "/file/path") - - let request6 = try Request(url: "https://127.0.0.1/") - XCTAssertEqual(request6.deconstructedURL.scheme, .https) - XCTAssertEqual( - request6.deconstructedURL.connectionTarget, - .ipAddress( - serialization: "127.0.0.1", - address: try! SocketAddress(ipAddress: "127.0.0.1", port: 443) - ) - ) - XCTAssertEqual(request6.deconstructedURL.uri, "/") - - let request7 = try Request(url: "https://127.0.0.1:9999/") - XCTAssertEqual(request7.deconstructedURL.scheme, .https) - XCTAssertEqual(request7.deconstructedURL.connectionTarget, .domain(name: "0x7F.1", port: 9999)) - XCTAssertEqual(request7.deconstructedURL.uri, "/") - - let request8 = try Request(url: "http://[::1]/") - XCTAssertEqual(request8.deconstructedURL.scheme, .http) - XCTAssertEqual( - request8.deconstructedURL.connectionTarget, - .ipAddress( - serialization: "[::1]", - address: try! SocketAddress(ipAddress: "::1", port: 80) - ) - ) - XCTAssertEqual(request8.deconstructedURL.uri, "/") - - let request9 = try Request(url: "http://[763e:61d9::6aca:3100:6274]:4242/foo/bar?baz") - XCTAssertEqual(request9.deconstructedURL.scheme, .http) - XCTAssertEqual( - request9.deconstructedURL.connectionTarget, - .ipAddress( - serialization: "[763e:61d9::6ACA:3100:6274]", - address: try! SocketAddress(ipAddress: "763e:61d9::6aca:3100:6274", port: 4242) - ) - ) - XCTAssertEqual(request9.deconstructedURL.uri, "/foo/bar?baz") - - // Some systems have quirks in their implementations of 'ntop' which cause them to write - // certain IPv6 addresses with embedded IPv4 parts (e.g. "::192.168.0.1" vs "::c0a8:1"). - // We want to make sure that our request formatting doesn't depend on the platform's quirks, - // so the serialization must be kept verbatim as it was given in the request. - let request10 = try Request(url: "http://[::c0a8:1]:4242/foo/bar?baz") - XCTAssertEqual(request10.deconstructedURL.scheme, .http) - XCTAssertEqual( - request10.deconstructedURL.connectionTarget, - .ipAddress( - serialization: "[::c0a8:1]", - address: try! SocketAddress(ipAddress: "::c0a8:1", port: 4242) - ) - ) - XCTAssertEqual(request10.deconstructedURL.uri, "/foo/bar?baz") - - let request11 = try Request(url: "http://[::c0a8:1]:4242/foo/bar?baz") - XCTAssertEqual(request11.deconstructedURL.scheme, .http) - XCTAssertEqual( - request11.deconstructedURL.connectionTarget, - .ipAddress( - serialization: "[::192.168.0.1]", - address: try! SocketAddress(ipAddress: "::192.168.0.1", port: 4242) - ) - ) - XCTAssertEqual(request11.deconstructedURL.uri, "/foo/bar?baz") - } - - func testHasSuffix() { - // Simple collection. - do { - let elements = (0...10) - XCTAssertTrue(elements.hasSuffix([8, 9, 10])) - XCTAssertTrue(elements.hasSuffix([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])) - XCTAssertTrue(elements.hasSuffix([10])) - XCTAssertTrue(elements.hasSuffix([])) - - XCTAssertFalse(elements.hasSuffix([8, 9, 10, 11])) - XCTAssertFalse(elements.hasSuffix([0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])) - XCTAssertFalse(elements.hasSuffix([9])) - XCTAssertFalse(elements.hasSuffix([0])) - } - // Single-element collection. - do { - let elements = [99] - XCTAssertTrue(elements.hasSuffix(["99"].lazy.map { Int($0)! })) - XCTAssertTrue(elements.hasSuffix([])) - XCTAssertFalse(elements.hasSuffix([98, 99])) - XCTAssertFalse(elements.hasSuffix([99, 99])) - XCTAssertFalse(elements.hasSuffix([99, 100])) - } - // Empty collection. - do { - let elements: [Int] = [] - XCTAssertTrue(elements.hasSuffix([])) - XCTAssertFalse(elements.hasSuffix([0])) - XCTAssertFalse(elements.hasSuffix([42])) - XCTAssertFalse(elements.hasSuffix([0, 0, 0])) - } - } - - /// test to verify that we actually share the same thread pool across all ``FileDownloadDelegate``s for a given ``HTTPClient`` - func testSharedThreadPoolIsIdenticalForAllDelegates() throws { - let httpBin = HTTPBin() - let httpClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup)) - defer { - XCTAssertNoThrow(try httpClient.syncShutdown(requiresCleanClose: true)) - XCTAssertNoThrow(try httpBin.shutdown()) - } - var request = try Request(url: "http://localhost/(httpBin.port)/events/10/content-length") - request.headers.add(name: "Accept", value: "text/event-stream") - - let filePaths = (0..<10).map { _ in - TemporaryFileHelpers.makeTemporaryFilePath() - } - defer { - for filePath in filePaths { - TemporaryFileHelpers.removeTemporaryFile(at: filePath) - } - } - let delegates = try filePaths.map { - try FileDownloadDelegate(path: $0) - } - - let resultFutures = delegates.map { delegate in - httpClient.execute( - request: request, - delegate: delegate - ).futureResult - } - _ = try EventLoopFuture.whenAllSucceed(resultFutures, on: self.clientGroup.next()).wait() - - let threadPools = delegates.map { $0._fileIOThreadPool } - let firstThreadPool = threadPools.first ?? nil - XCTAssert(threadPools.dropFirst().allSatisfy { $0 === firstThreadPool }) - } -} - -extension HTTPClient.Configuration { - func enableFastFailureModeForTesting() -> Self { - var copy = self - copy.networkFrameworkWaitForConnectivity = false - copy.connectionPool.retryConnectionEstablishment = false - return copy - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTPClientNIOTSTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientNIOTSTests.swift deleted file mode 100644 index 4c2d24dc4..000000000 --- a/Tests/AsyncHTTPClientTests/HTTPClientNIOTSTests.swift +++ /dev/null @@ -1,192 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2018-2019 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOConcurrencyHelpers -import NIOCore -import NIOPosix -import NIOSSL -import NIOTransportServices -import XCTest - -@testable import AsyncHTTPClient - -#if canImport(Network) -import Network -#endif - -class HTTPClientNIOTSTests: XCTestCase { - var clientGroup: EventLoopGroup! - - override func setUp() { - XCTAssertNil(self.clientGroup) - self.clientGroup = getDefaultEventLoopGroup(numberOfThreads: 3) - } - - override func tearDown() { - XCTAssertNotNil(self.clientGroup) - XCTAssertNoThrow(try self.clientGroup.syncShutdownGracefully()) - self.clientGroup = nil - } - - func testCorrectEventLoopGroup() { - let httpClient = HTTPClient(eventLoopGroupProvider: .singleton) - defer { - XCTAssertNoThrow(try httpClient.syncShutdown()) - } - #if canImport(Network) - if #available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) { - XCTAssertTrue(httpClient.eventLoopGroup is NIOTSEventLoopGroup) - return - } - #endif - XCTAssertTrue(httpClient.eventLoopGroup is MultiThreadedEventLoopGroup) - } - - func testTLSFailError() { - guard isTestingNIOTS() else { return } - - let httpBin = HTTPBin(.http1_1(ssl: true)) - let config = HTTPClient.Configuration() - .enableFastFailureModeForTesting() - let httpClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: config - ) - defer { - XCTAssertNoThrow(try httpClient.syncShutdown(requiresCleanClose: true)) - XCTAssertNoThrow(try httpBin.shutdown()) - } - - #if canImport(Network) - do { - _ = try httpClient.get(url: "https://localhost/(httpBin.port)/get").wait() - XCTFail("This should have failed") - } catch let error as HTTPClient.NWTLSError { - XCTAssert( - error.status == errSSLHandshakeFail || error.status == errSSLBadCert, - "unexpected NWTLSError with status \(error.status)" - ) - } catch { - XCTFail("Error should have been NWTLSError not \(type(of: error))") - } - #else - XCTFail("wrong OS") - #endif - } - - func testConnectionFailsFastError() { - guard isTestingNIOTS() else { return } - #if canImport(Network) - let httpBin = HTTPBin(.http1_1(ssl: false)) - let config = HTTPClient.Configuration() - .enableFastFailureModeForTesting() - - let httpClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: config - ) - - defer { - XCTAssertNoThrow(try httpClient.syncShutdown(requiresCleanClose: true)) - } - - let port = httpBin.port - XCTAssertNoThrow(try httpBin.shutdown()) - - XCTAssertThrowsError(try httpClient.get(url: "http://localhost/(port)/get").wait()) { - XCTAssertTrue($0 is NWError) - } - #endif - } - - func testConnectionFailError() { - guard isTestingNIOTS() else { return } - #if canImport(Network) - let httpBin = HTTPBin(.http1_1(ssl: false)) - let httpClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: .init( - timeout: .init( - connect: .milliseconds(100), - read: .milliseconds(100) - ) - ) - ) - - defer { - XCTAssertNoThrow(try httpClient.syncShutdown(requiresCleanClose: true)) - } - - let port = httpBin.port - XCTAssertNoThrow(try httpBin.shutdown()) - - XCTAssertThrowsError(try httpClient.get(url: "http://localhost/(port)/get").wait()) { - if let httpClientError = $0 as? HTTPClientError { - XCTAssertEqual(httpClientError, .connectTimeout) - } else if let posixError = $0 as? HTTPClient.NWPOSIXError { - XCTAssertEqual(posixError.errorCode, .ECONNREFUSED) - } else { - XCTFail("unexpected error \($0)") - } - } - #endif - } - - func testTLSVersionError() { - guard isTestingNIOTS() else { return } - #if canImport(Network) - let httpBin = HTTPBin(.http1_1(ssl: true)) - var tlsConfig = TLSConfiguration.makeClientConfiguration() - tlsConfig.certificateVerification = .none - tlsConfig.minimumTLSVersion = .tlsv11 - tlsConfig.maximumTLSVersion = .tlsv1 - - let clientConfig = HTTPClient.Configuration(tlsConfiguration: tlsConfig) - .enableFastFailureModeForTesting() - let httpClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: clientConfig - ) - defer { - XCTAssertNoThrow(try httpClient.syncShutdown(requiresCleanClose: true)) - XCTAssertNoThrow(try httpBin.shutdown()) - } - - XCTAssertThrowsError(try httpClient.get(url: "https://localhost/(httpBin.port)/get").wait()) { error in - XCTAssertEqual((error as? HTTPClient.NWTLSError)?.status, errSSLHandshakeFail) - } - #endif - } - - func testTrustRootCertificateLoadFail() { - guard isTestingNIOTS() else { return } - #if canImport(Network) - if #available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) { - var tlsConfig = TLSConfiguration.makeClientConfiguration() - tlsConfig.trustRoots = .file("not/a/certificate") - - XCTAssertThrowsError(try tlsConfig.getNWProtocolTLSOptions(serverNameIndicatorOverride: nil)) { error in - switch error { - case let error as NIOSSL.NIOSSLError where error == .failedToLoadCertificate: - break - default: - XCTFail("\(error)") - } - } - } else { - XCTFail("should be impossible") - } - #endif - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift deleted file mode 100644 index 54467aab7..000000000 --- a/Tests/AsyncHTTPClientTests/HTTPClientRequestTests.swift +++ /dev/null @@ -1,819 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2018-2019 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Algorithms -import NIOConcurrencyHelpers -import NIOCore -import NIOHTTP1 -import XCTest - -@testable import AsyncHTTPClient - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -class HTTPClientRequestTests: XCTestCase { - private typealias Request = HTTPClientRequest - - private typealias PreparedRequest = HTTPClientRequest.Prepared - - func testCustomHeadersAreRespected() { - XCTAsyncTest { - var request = Request(url: "https://example.com/get") - request.headers = [ - "custom-header": "custom-header-value" - ] - var preparedRequest: PreparedRequest? - XCTAssertNoThrow(preparedRequest = try PreparedRequest(request)) - guard let preparedRequest = preparedRequest else { return } - - XCTAssertEqual( - preparedRequest.poolKey, - .init( - scheme: .https, - connectionTarget: .domain(name: "example.com", port: 443), - tlsConfiguration: nil, - serverNameIndicatorOverride: nil - ) - ) - XCTAssertEqual( - preparedRequest.head, - .init( - version: .http1_1, - method: .GET, - uri: "/get", - headers: [ - "host": "example.com", - "custom-header": "custom-header-value", - ] - ) - ) - XCTAssertEqual( - preparedRequest.requestFramingMetadata, - .init( - connectionClose: false, - body: .fixedSize(0) - ) - ) - guard let buffer = await XCTAssertNoThrowWithResult(try await preparedRequest.body.read()) else { return } - XCTAssertEqual(buffer, ByteBuffer()) - } - } - - func testBasicAuth() { - XCTAsyncTest { - var request = Request(url: "https://example.com/get") - request.setBasicAuth(username: "foo", password: "bar") - var preparedRequest: PreparedRequest? - XCTAssertNoThrow(preparedRequest = try PreparedRequest(request)) - guard let preparedRequest = preparedRequest else { return } - XCTAssertEqual(preparedRequest.head.headers.first(name: "Authorization")!, "Basic Zm9vOmJhcg==") - } - } - - func testUnixScheme() { - XCTAsyncTest { - var request = Request(url: "unix://%2Fexample%2Ffolder.sock/some_path") - request.headers = ["custom-header": "custom-value"] - var preparedRequest: PreparedRequest? - XCTAssertNoThrow(preparedRequest = try PreparedRequest(request)) - guard let preparedRequest = preparedRequest else { return } - - XCTAssertEqual( - preparedRequest.poolKey, - .init( - scheme: .unix, - connectionTarget: .unixSocket(path: "/some_path"), - tlsConfiguration: nil, - serverNameIndicatorOverride: nil - ) - ) - XCTAssertEqual( - preparedRequest.head, - .init( - version: .http1_1, - method: .GET, - uri: "/", - headers: ["custom-header": "custom-value"] - ) - ) - XCTAssertEqual( - preparedRequest.requestFramingMetadata, - .init( - connectionClose: false, - body: .fixedSize(0) - ) - ) - guard let buffer = await XCTAssertNoThrowWithResult(try await preparedRequest.body.read()) else { return } - XCTAssertEqual(buffer, ByteBuffer()) - } - } - - func testHTTPUnixScheme() { - XCTAsyncTest { - var request = Request(url: "http+unix://%2Fexample%2Ffolder.sock/some_path") - request.headers = ["custom-header": "custom-value"] - var preparedRequest: PreparedRequest? - XCTAssertNoThrow(preparedRequest = try PreparedRequest(request)) - guard let preparedRequest = preparedRequest else { return } - - XCTAssertEqual( - preparedRequest.poolKey, - .init( - scheme: .httpUnix, - connectionTarget: .unixSocket(path: "/example/folder.sock"), - tlsConfiguration: nil, - serverNameIndicatorOverride: nil - ) - ) - XCTAssertEqual( - preparedRequest.head, - .init( - version: .http1_1, - method: .GET, - uri: "/some_path", - headers: ["custom-header": "custom-value"] - ) - ) - XCTAssertEqual( - preparedRequest.requestFramingMetadata, - .init( - connectionClose: false, - body: .fixedSize(0) - ) - ) - guard let buffer = await XCTAssertNoThrowWithResult(try await preparedRequest.body.read()) else { return } - XCTAssertEqual(buffer, ByteBuffer()) - } - } - - func testHTTPSUnixScheme() { - XCTAsyncTest { - var request = Request(url: "https+unix://%2Fexample%2Ffolder.sock/some_path") - request.headers = ["custom-header": "custom-value"] - var preparedRequest: PreparedRequest? - XCTAssertNoThrow(preparedRequest = try PreparedRequest(request)) - guard let preparedRequest = preparedRequest else { return } - - XCTAssertEqual( - preparedRequest.poolKey, - .init( - scheme: .httpsUnix, - connectionTarget: .unixSocket(path: "/example/folder.sock"), - tlsConfiguration: nil, - serverNameIndicatorOverride: nil - ) - ) - XCTAssertEqual( - preparedRequest.head, - .init( - version: .http1_1, - method: .GET, - uri: "/some_path", - headers: ["custom-header": "custom-value"] - ) - ) - XCTAssertEqual( - preparedRequest.requestFramingMetadata, - .init( - connectionClose: false, - body: .fixedSize(0) - ) - ) - guard let buffer = await XCTAssertNoThrowWithResult(try await preparedRequest.body.read()) else { return } - XCTAssertEqual(buffer, ByteBuffer()) - } - } - - func testGetWithoutBody() { - XCTAsyncTest { - let request = Request(url: "https://example.com/get") - var preparedRequest: PreparedRequest? - XCTAssertNoThrow(preparedRequest = try PreparedRequest(request)) - guard let preparedRequest = preparedRequest else { return } - - XCTAssertEqual( - preparedRequest.poolKey, - .init( - scheme: .https, - connectionTarget: .domain(name: "example.com", port: 443), - tlsConfiguration: nil, - serverNameIndicatorOverride: nil - ) - ) - XCTAssertEqual( - preparedRequest.head, - .init( - version: .http1_1, - method: .GET, - uri: "/get", - headers: ["host": "example.com"] - ) - ) - XCTAssertEqual( - preparedRequest.requestFramingMetadata, - .init( - connectionClose: false, - body: .fixedSize(0) - ) - ) - guard let buffer = await XCTAssertNoThrowWithResult(try await preparedRequest.body.read()) else { return } - XCTAssertEqual(buffer, ByteBuffer()) - } - } - - func testPostWithoutBody() { - XCTAsyncTest { - var request = Request(url: "http://example.com/post") - request.method = .POST - var preparedRequest: PreparedRequest? - XCTAssertNoThrow(preparedRequest = try PreparedRequest(request)) - guard let preparedRequest = preparedRequest else { return } - - XCTAssertEqual( - preparedRequest.poolKey, - .init( - scheme: .http, - connectionTarget: .domain(name: "example.com", port: 80), - tlsConfiguration: nil, - serverNameIndicatorOverride: nil - ) - ) - XCTAssertEqual( - preparedRequest.head, - .init( - version: .http1_1, - method: .POST, - uri: "/post", - headers: [ - "host": "example.com", - "content-length": "0", - ] - ) - ) - XCTAssertEqual( - preparedRequest.requestFramingMetadata, - .init( - connectionClose: false, - body: .fixedSize(0) - ) - ) - - guard let buffer = await XCTAssertNoThrowWithResult(try await preparedRequest.body.read()) else { return } - XCTAssertEqual(buffer, ByteBuffer()) - } - } - - func testPostWithEmptyByteBuffer() { - XCTAsyncTest { - var request = Request(url: "http://example.com/post") - request.method = .POST - request.body = .bytes(ByteBuffer()) - var preparedRequest: PreparedRequest? - XCTAssertNoThrow(preparedRequest = try PreparedRequest(request)) - guard let preparedRequest = preparedRequest else { return } - - XCTAssertEqual( - preparedRequest.poolKey, - .init( - scheme: .http, - connectionTarget: .domain(name: "example.com", port: 80), - tlsConfiguration: nil, - serverNameIndicatorOverride: nil - ) - ) - XCTAssertEqual( - preparedRequest.head, - .init( - version: .http1_1, - method: .POST, - uri: "/post", - headers: [ - "host": "example.com", - "content-length": "0", - ] - ) - ) - XCTAssertEqual( - preparedRequest.requestFramingMetadata, - .init( - connectionClose: false, - body: .fixedSize(0) - ) - ) - - guard let buffer = await XCTAssertNoThrowWithResult(try await preparedRequest.body.read()) else { return } - XCTAssertEqual(buffer, ByteBuffer()) - } - } - - func testPostWithByteBuffer() { - XCTAsyncTest { - var request = Request(url: "http://example.com/post") - request.method = .POST - request.body = .bytes(.init(string: "post body")) - var preparedRequest: PreparedRequest? - XCTAssertNoThrow(preparedRequest = try PreparedRequest(request)) - guard let preparedRequest = preparedRequest else { return } - - XCTAssertEqual( - preparedRequest.poolKey, - .init( - scheme: .http, - connectionTarget: .domain(name: "example.com", port: 80), - tlsConfiguration: nil, - serverNameIndicatorOverride: nil - ) - ) - XCTAssertEqual( - preparedRequest.head, - .init( - version: .http1_1, - method: .POST, - uri: "/post", - headers: [ - "host": "example.com", - "content-length": "9", - ] - ) - ) - XCTAssertEqual( - preparedRequest.requestFramingMetadata, - .init( - connectionClose: false, - body: .fixedSize(9) - ) - ) - guard let buffer = await XCTAssertNoThrowWithResult(try await preparedRequest.body.read()) else { return } - XCTAssertEqual(buffer, .init(string: "post body")) - } - } - - func testPostWithSequenceOfUnknownLength() { - XCTAsyncTest { - var request = Request(url: "http://example.com/post") - request.method = .POST - let sequence = AnySendableSequence(ByteBuffer(string: "post body").readableBytesView) - request.body = .bytes(sequence, length: .unknown) - var preparedRequest: PreparedRequest? - XCTAssertNoThrow(preparedRequest = try PreparedRequest(request)) - guard let preparedRequest = preparedRequest else { return } - - XCTAssertEqual( - preparedRequest.poolKey, - .init( - scheme: .http, - connectionTarget: .domain(name: "example.com", port: 80), - tlsConfiguration: nil, - serverNameIndicatorOverride: nil - ) - ) - XCTAssertEqual( - preparedRequest.head, - .init( - version: .http1_1, - method: .POST, - uri: "/post", - headers: [ - "host": "example.com", - "transfer-encoding": "chunked", - ] - ) - ) - XCTAssertEqual( - preparedRequest.requestFramingMetadata, - .init( - connectionClose: false, - body: .stream - ) - ) - guard let buffer = await XCTAssertNoThrowWithResult(try await preparedRequest.body.read()) else { return } - XCTAssertEqual(buffer, .init(string: "post body")) - } - } - - func testPostWithSequenceWithFixedLength() { - XCTAsyncTest { - var request = Request(url: "http://example.com/post") - request.method = .POST - - let sequence = AnySendableSequence(ByteBuffer(string: "post body").readableBytesView) - request.body = .bytes(sequence, length: .known(Int64(9))) - var preparedRequest: PreparedRequest? - XCTAssertNoThrow(preparedRequest = try PreparedRequest(request)) - guard let preparedRequest = preparedRequest else { return } - - XCTAssertEqual( - preparedRequest.poolKey, - .init( - scheme: .http, - connectionTarget: .domain(name: "example.com", port: 80), - tlsConfiguration: nil, - serverNameIndicatorOverride: nil - ) - ) - XCTAssertEqual( - preparedRequest.head, - .init( - version: .http1_1, - method: .POST, - uri: "/post", - headers: [ - "host": "example.com", - "content-length": "9", - ] - ) - ) - XCTAssertEqual( - preparedRequest.requestFramingMetadata, - .init( - connectionClose: false, - body: .fixedSize(9) - ) - ) - guard let buffer = await XCTAssertNoThrowWithResult(try await preparedRequest.body.read()) else { return } - XCTAssertEqual(buffer, .init(string: "post body")) - } - } - - func testPostWithRandomAccessCollection() { - XCTAsyncTest { - var request = Request(url: "http://example.com/post") - request.method = .POST - let collection = ByteBuffer(string: "post body").readableBytesView - request.body = .bytes(collection) - var preparedRequest: PreparedRequest? - XCTAssertNoThrow(preparedRequest = try PreparedRequest(request)) - guard let preparedRequest = preparedRequest else { return } - - XCTAssertEqual( - preparedRequest.poolKey, - .init( - scheme: .http, - connectionTarget: .domain(name: "example.com", port: 80), - tlsConfiguration: nil, - serverNameIndicatorOverride: nil - ) - ) - XCTAssertEqual( - preparedRequest.head, - .init( - version: .http1_1, - method: .POST, - uri: "/post", - headers: [ - "host": "example.com", - "content-length": "9", - ] - ) - ) - XCTAssertEqual( - preparedRequest.requestFramingMetadata, - .init( - connectionClose: false, - body: .fixedSize(9) - ) - ) - guard let buffer = await XCTAssertNoThrowWithResult(try await preparedRequest.body.read()) else { return } - XCTAssertEqual(buffer, .init(string: "post body")) - } - } - - func testPostWithAsyncSequenceOfUnknownLength() { - XCTAsyncTest { - var request = Request(url: "http://example.com/post") - request.method = .POST - let asyncSequence = ByteBuffer(string: "post body") - .readableBytesView - .uncheckedSendableChunks(ofCount: 2) - .async - .map { ByteBuffer($0) } - - request.body = .stream(asyncSequence, length: .unknown) - var preparedRequest: PreparedRequest? - XCTAssertNoThrow(preparedRequest = try PreparedRequest(request)) - guard let preparedRequest = preparedRequest else { return } - - XCTAssertEqual( - preparedRequest.poolKey, - .init( - scheme: .http, - connectionTarget: .domain(name: "example.com", port: 80), - tlsConfiguration: nil, - serverNameIndicatorOverride: nil - ) - ) - XCTAssertEqual( - preparedRequest.head, - .init( - version: .http1_1, - method: .POST, - uri: "/post", - headers: [ - "host": "example.com", - "transfer-encoding": "chunked", - ] - ) - ) - XCTAssertEqual( - preparedRequest.requestFramingMetadata, - .init( - connectionClose: false, - body: .stream - ) - ) - guard let buffer = await XCTAssertNoThrowWithResult(try await preparedRequest.body.read()) else { return } - XCTAssertEqual(buffer, .init(string: "post body")) - } - } - - func testPostWithAsyncSequenceWithKnownLength() { - XCTAsyncTest { - var request = Request(url: "http://example.com/post") - request.method = .POST - let asyncSequence = ByteBuffer(string: "post body") - .readableBytesView - .uncheckedSendableChunks(ofCount: 2) - .async - .map { ByteBuffer($0) } - - request.body = .stream(asyncSequence, length: .known(Int64(9))) - var preparedRequest: PreparedRequest? - XCTAssertNoThrow(preparedRequest = try PreparedRequest(request)) - guard let preparedRequest = preparedRequest else { return } - - XCTAssertEqual( - preparedRequest.poolKey, - .init( - scheme: .http, - connectionTarget: .domain(name: "example.com", port: 80), - tlsConfiguration: nil, - serverNameIndicatorOverride: nil - ) - ) - XCTAssertEqual( - preparedRequest.head, - .init( - version: .http1_1, - method: .POST, - uri: "/post", - headers: [ - "host": "example.com", - "content-length": "9", - ] - ) - ) - XCTAssertEqual( - preparedRequest.requestFramingMetadata, - .init( - connectionClose: false, - body: .fixedSize(9) - ) - ) - guard let buffer = await XCTAssertNoThrowWithResult(try await preparedRequest.body.read()) else { return } - XCTAssertEqual(buffer, .init(string: "post body")) - } - } - - func testChunkingRandomAccessCollection() async throws { - let body = try await HTTPClientRequest.Body.bytes( - Array(repeating: 0, count: bagOfBytesToByteBufferConversionChunkSize) - + Array(repeating: 1, count: bagOfBytesToByteBufferConversionChunkSize) - + Array(repeating: 2, count: bagOfBytesToByteBufferConversionChunkSize) - ).collect() - - let expectedChunks = [ - ByteBuffer(repeating: 0, count: bagOfBytesToByteBufferConversionChunkSize), - ByteBuffer(repeating: 1, count: bagOfBytesToByteBufferConversionChunkSize), - ByteBuffer(repeating: 2, count: bagOfBytesToByteBufferConversionChunkSize), - ] - - XCTAssertEqual(body, expectedChunks) - } - - func testChunkingCollection() async throws { - let body = try await HTTPClientRequest.Body.bytes( - (String(repeating: "0", count: bagOfBytesToByteBufferConversionChunkSize) - + String(repeating: "1", count: bagOfBytesToByteBufferConversionChunkSize) - + String(repeating: "2", count: bagOfBytesToByteBufferConversionChunkSize)).utf8, - length: .known(Int64(bagOfBytesToByteBufferConversionChunkSize * 3)) - ).collect() - - let expectedChunks = [ - ByteBuffer(repeating: UInt8(ascii: "0"), count: bagOfBytesToByteBufferConversionChunkSize), - ByteBuffer(repeating: UInt8(ascii: "1"), count: bagOfBytesToByteBufferConversionChunkSize), - ByteBuffer(repeating: UInt8(ascii: "2"), count: bagOfBytesToByteBufferConversionChunkSize), - ] - - XCTAssertEqual(body, expectedChunks) - } - - func testChunkingSequenceThatDoesNotImplementWithContiguousStorageIfAvailable() async throws { - let bagOfBytesToByteBufferConversionChunkSize = 8 - let body = try await HTTPClientRequest.Body._bytes( - AnySendableSequence( - Array(repeating: 0, count: bagOfBytesToByteBufferConversionChunkSize) - + Array(repeating: 1, count: bagOfBytesToByteBufferConversionChunkSize) - ), - length: .known(Int64(bagOfBytesToByteBufferConversionChunkSize * 3)), - bagOfBytesToByteBufferConversionChunkSize: bagOfBytesToByteBufferConversionChunkSize, - byteBufferMaxSize: byteBufferMaxSize - ).collect() - - let expectedChunks = [ - ByteBuffer(repeating: 0, count: bagOfBytesToByteBufferConversionChunkSize), - ByteBuffer(repeating: 1, count: bagOfBytesToByteBufferConversionChunkSize), - ] - - XCTAssertEqual(body, expectedChunks) - } - - func testChunkingSequenceFastPath() async throws { - func makeBytes() -> some Sequence & Sendable { - Array(repeating: 0, count: bagOfBytesToByteBufferConversionChunkSize) - + Array(repeating: 1, count: bagOfBytesToByteBufferConversionChunkSize) - + Array(repeating: 2, count: bagOfBytesToByteBufferConversionChunkSize) - } - let body = try await HTTPClientRequest.Body.bytes( - makeBytes(), - length: .known(Int64(bagOfBytesToByteBufferConversionChunkSize * 3)) - ).collect() - - var firstChunk = ByteBuffer(repeating: 0, count: bagOfBytesToByteBufferConversionChunkSize) - firstChunk.writeImmutableBuffer(ByteBuffer(repeating: 1, count: bagOfBytesToByteBufferConversionChunkSize)) - firstChunk.writeImmutableBuffer(ByteBuffer(repeating: 2, count: bagOfBytesToByteBufferConversionChunkSize)) - let expectedChunks = [ - firstChunk - ] - - XCTAssertEqual(body, expectedChunks) - } - - func testChunkingSequenceFastPathExceedingByteBufferMaxSize() async throws { - let bagOfBytesToByteBufferConversionChunkSize = 8 - let byteBufferMaxSize = 16 - func makeBytes() -> some Sequence & Sendable { - Array(repeating: 0, count: bagOfBytesToByteBufferConversionChunkSize) - + Array(repeating: 1, count: bagOfBytesToByteBufferConversionChunkSize) - + Array(repeating: 2, count: bagOfBytesToByteBufferConversionChunkSize) - } - let body = try await HTTPClientRequest.Body._bytes( - makeBytes(), - length: .known(Int64(bagOfBytesToByteBufferConversionChunkSize * 3)), - bagOfBytesToByteBufferConversionChunkSize: bagOfBytesToByteBufferConversionChunkSize, - byteBufferMaxSize: byteBufferMaxSize - ).collect() - - var firstChunk = ByteBuffer(repeating: 0, count: bagOfBytesToByteBufferConversionChunkSize) - firstChunk.writeImmutableBuffer(ByteBuffer(repeating: 1, count: bagOfBytesToByteBufferConversionChunkSize)) - let secondChunk = ByteBuffer(repeating: 2, count: bagOfBytesToByteBufferConversionChunkSize) - let expectedChunks = [ - firstChunk, - secondChunk, - ] - - XCTAssertEqual(body, expectedChunks) - } - - func testBodyStringChunking() throws { - let body = try HTTPClient.Body.string( - String(repeating: "0", count: bagOfBytesToByteBufferConversionChunkSize) - + String(repeating: "1", count: bagOfBytesToByteBufferConversionChunkSize) - + String(repeating: "2", count: bagOfBytesToByteBufferConversionChunkSize) - ).collect().wait() - - let expectedChunks = [ - ByteBuffer(), // We're currently emitting an empty chunk first. - ByteBuffer(repeating: UInt8(ascii: "0"), count: bagOfBytesToByteBufferConversionChunkSize), - ByteBuffer(repeating: UInt8(ascii: "1"), count: bagOfBytesToByteBufferConversionChunkSize), - ByteBuffer(repeating: UInt8(ascii: "2"), count: bagOfBytesToByteBufferConversionChunkSize), - ] - - XCTAssertEqual(body, expectedChunks) - } - - func testBodyChunkingRandomAccessCollection() throws { - let body = try HTTPClient.Body.bytes( - Array(repeating: 0, count: bagOfBytesToByteBufferConversionChunkSize) - + Array(repeating: 1, count: bagOfBytesToByteBufferConversionChunkSize) - + Array(repeating: 2, count: bagOfBytesToByteBufferConversionChunkSize) - ).collect().wait() - - let expectedChunks = [ - ByteBuffer(), // We're currently emitting an empty chunk first. - ByteBuffer(repeating: 0, count: bagOfBytesToByteBufferConversionChunkSize), - ByteBuffer(repeating: 1, count: bagOfBytesToByteBufferConversionChunkSize), - ByteBuffer(repeating: 2, count: bagOfBytesToByteBufferConversionChunkSize), - ] - - XCTAssertEqual(body, expectedChunks) - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension AsyncSequence { - func collect() async throws -> [Element] { - try await self.reduce(into: []) { $0 += CollectionOfOne($1) } - } -} - -extension HTTPClient.Body { - func collect() -> EventLoopFuture<[ByteBuffer]> { - let eelg = EmbeddedEventLoopGroup(loops: 1) - let el = eelg.next() - let body = NIOLockedValueBox<[ByteBuffer]>([]) - let writer = StreamWriter { - switch $0 { - case .byteBuffer(let byteBuffer): - body.withLockedValue { $0.append(byteBuffer) } - case .fileRegion: - fatalError("file region not supported") - } - return el.makeSucceededVoidFuture() - } - return self.stream(writer).map { _ in body.withLockedValue { $0 } } - } -} - -private struct LengthMismatch: Error { - var announcedLength: Int64 - var actualLength: Int64 -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension Optional where Wrapped == HTTPClientRequest.Prepared.Body { - /// Accumulates all data from `self` into a single `ByteBuffer` and checks that the user specified length matches - /// the length of the accumulated data. - fileprivate func read() async throws -> ByteBuffer { - switch self { - case .none: - return ByteBuffer() - case .byteBuffer(let buffer): - return buffer - case .sequence(let announcedLength, _, let generate): - let buffer = generate(ByteBufferAllocator()) - if case .known(let announcedLength) = announcedLength, - announcedLength != Int64(buffer.readableBytes) - { - throw LengthMismatch(announcedLength: announcedLength, actualLength: Int64(buffer.readableBytes)) - } - return buffer - case .asyncSequence(length: let announcedLength, let makeAsyncIterator): - var accumulatedBuffer = ByteBuffer() - let generate = makeAsyncIterator() - while var buffer = try await generate(ByteBufferAllocator()) { - accumulatedBuffer.writeBuffer(&buffer) - } - if case .known(let announcedLength) = announcedLength, - announcedLength != Int64(accumulatedBuffer.readableBytes) - { - throw LengthMismatch( - announcedLength: announcedLength, - actualLength: Int64(accumulatedBuffer.readableBytes) - ) - } - return accumulatedBuffer - } - } -} - -// swift-algorithms hasn't adopted Sendable yet. By inspection ChunksOfCountCollection should be -// Sendable assuming the underlying collection is. This wrapper allows us to avoid a blanket -// preconcurrency import of the Algorithms module. -struct UncheckedSendableChunksOfCountCollection: Collection, @unchecked Sendable -where Base: Sendable { - typealias Element = Base.SubSequence - typealias Index = ChunksOfCountCollection.Index - - private let underlying: ChunksOfCountCollection - - init(_ underlying: ChunksOfCountCollection) { - self.underlying = underlying - } - - var startIndex: Index { self.underlying.startIndex } - var endIndex: Index { self.underlying.endIndex } - - subscript(position: Index) -> Base.SubSequence { - self.underlying[position] - } - - func index(after i: Index) -> Index { - self.underlying.index(after: i) - } -} - -extension Collection where Self: Sendable { - func uncheckedSendableChunks(ofCount count: Int) -> UncheckedSendableChunksOfCountCollection { - UncheckedSendableChunksOfCountCollection(self.chunks(ofCount: count)) - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTPClientResponseTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientResponseTests.swift deleted file mode 100644 index 7dcc4efe6..000000000 --- a/Tests/AsyncHTTPClientTests/HTTPClientResponseTests.swift +++ /dev/null @@ -1,64 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2023 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOCore -import NIOHTTP1 -import XCTest - -@testable import AsyncHTTPClient - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -final class HTTPClientResponseTests: XCTestCase { - func testSimpleResponse() { - let response = HTTPClientResponse.expectedContentLength( - requestMethod: .GET, - headers: ["content-length": "1025"], - status: .ok - ) - XCTAssertEqual(response, 1025) - } - - func testSimpleResponseNotModified() { - let response = HTTPClientResponse.expectedContentLength( - requestMethod: .GET, - headers: ["content-length": "1025"], - status: .notModified - ) - XCTAssertEqual(response, 0) - } - - func testSimpleResponseHeadRequestMethod() { - let response = HTTPClientResponse.expectedContentLength( - requestMethod: .HEAD, - headers: ["content-length": "1025"], - status: .ok - ) - XCTAssertEqual(response, 0) - } - - func testResponseNoContentLengthHeader() { - let response = HTTPClientResponse.expectedContentLength(requestMethod: .GET, headers: [:], status: .ok) - XCTAssertEqual(response, nil) - } - - func testResponseInvalidInteger() { - let response = HTTPClientResponse.expectedContentLength( - requestMethod: .GET, - headers: ["content-length": "none"], - status: .ok - ) - XCTAssertEqual(response, nil) - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTPClientTestUtils.swift b/Tests/AsyncHTTPClientTests/HTTPClientTestUtils.swift deleted file mode 100644 index f9917c885..000000000 --- a/Tests/AsyncHTTPClientTests/HTTPClientTestUtils.swift +++ /dev/null @@ -1,1705 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2018-2019 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Atomics -import Foundation -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOEmbedded -import NIOFoundationCompat -import NIOHPACK -import NIOHTTP1 -import NIOHTTP2 -import NIOHTTPCompression -import NIOPosix -import NIOSSL -import NIOTLS -import NIOTransportServices -import XCTest - -@testable import AsyncHTTPClient - -#if canImport(xlocale) -import xlocale -#elseif canImport(locale_h) -import locale_h -#elseif canImport(Darwin) -import Darwin -#elseif canImport(Musl) -import Musl -#elseif canImport(Android) -import Android -#elseif canImport(Glibc) -import Glibc -#endif - -/// Are we testing NIO Transport services -func isTestingNIOTS() -> Bool { - #if canImport(Network) - return ProcessInfo.processInfo.environment["DISABLE_TS_TESTS"] != "true" - #else - return false - #endif -} - -func getDefaultEventLoopGroup(numberOfThreads: Int) -> EventLoopGroup { - #if canImport(Network) - if #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *), - isTestingNIOTS() - { - return NIOTSEventLoopGroup(loopCount: numberOfThreads, defaultQoS: .default) - } - #endif - return MultiThreadedEventLoopGroup(numberOfThreads: numberOfThreads) -} - -let canBindIPv6Loopback: Bool = { - let elg = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { try! elg.syncShutdownGracefully() } - let serverChannel = try? ServerBootstrap(group: elg) - .serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) - .bind(host: "::1", port: 0) - .wait() - let didBind = (serverChannel != nil) - try! serverChannel?.close().wait() - return didBind -}() - -/// Runs the given block in the context of a non-English C locale (in this case, German). -/// Throws an XCTSkip error if the locale is not supported by the system. -func withCLocaleSetToGerman(_ body: () throws -> Void) throws { - guard let germanLocale = newlocale(LC_TIME_MASK | LC_NUMERIC_MASK, "de_DE", nil) else { - if errno == ENOENT { - throw XCTSkip("System does not support locale de_DE") - } else { - XCTFail("Failed to create locale de_DE") - return - } - } - defer { freelocale(germanLocale) } - - let oldLocale = uselocale(germanLocale) - defer { _ = uselocale(oldLocale) } - try body() -} - -final class TestHTTPDelegate: HTTPClientResponseDelegate { - typealias Response = Void - - init(backpressureEventLoop: EventLoop? = nil) { - self.state = NIOLockedValueBox(MutableState(backpressureEventLoop: backpressureEventLoop)) - } - - enum State { - case idle - case head(HTTPResponseHead) - case body(HTTPResponseHead, ByteBuffer) - case end - case error(Error) - } - - struct MutableState: Sendable { - var state: State = .idle - var backpressureEventLoop: EventLoop? - } - - let state: NIOLockedValueBox - - func didReceiveHead(task: HTTPClient.Task, _ head: HTTPResponseHead) -> EventLoopFuture { - let eventLoop = self.state.withLockedValue { - $0.state = .head(head) - return ($0.backpressureEventLoop ?? task.eventLoop) - } - - return eventLoop.makeSucceededVoidFuture() - } - - func didReceiveBodyPart(task: HTTPClient.Task, _ buffer: ByteBuffer) -> EventLoopFuture { - let eventLoop = self.state.withLockedValue { - switch $0.state { - case .head(let head): - $0.state = .body(head, buffer) - case .body(let head, var body): - var buffer = buffer - body.writeBuffer(&buffer) - $0.state = .body(head, body) - default: - preconditionFailure("expecting head or body") - } - return ($0.backpressureEventLoop ?? task.eventLoop) - } - - return eventLoop.makeSucceededVoidFuture() - } - - func didFinishRequest(task: HTTPClient.Task) throws {} -} - -final class CountingDelegate: HTTPClientResponseDelegate { - typealias Response = Int - - private let _count = NIOLockedValueBox(0) - - func didReceiveBodyPart(task: HTTPClient.Task, _ buffer: ByteBuffer) -> EventLoopFuture { - let str = buffer.getString(at: 0, length: buffer.readableBytes) - if str?.starts(with: "id:") ?? false { - self._count.withLockedValue { $0 += 1 } - } - return task.eventLoop.makeSucceededFuture(()) - } - - func didFinishRequest(task: HTTPClient.Task) throws -> Int { - self._count.withLockedValue { $0 } - } -} - -final class DelayOnHeadDelegate: HTTPClientResponseDelegate { - typealias Response = ByteBuffer - - let eventLoop: EventLoop - let didReceiveHead: @Sendable (HTTPResponseHead, EventLoopPromise) -> Void - - struct State: Sendable { - var data: ByteBuffer - var mayReceiveData = false - var expectError = false - } - - private let state: NIOLockedValueBox - - init(eventLoop: EventLoop, didReceiveHead: @escaping @Sendable (HTTPResponseHead, EventLoopPromise) -> Void) { - self.eventLoop = eventLoop - self.didReceiveHead = didReceiveHead - self.state = NIOLockedValueBox(State(data: ByteBuffer())) - } - - func didReceiveHead(task: HTTPClient.Task, _ head: HTTPResponseHead) -> EventLoopFuture { - self.state.withLockedValue { - XCTAssertFalse($0.mayReceiveData) - XCTAssertFalse($0.expectError) - } - - let promise = self.eventLoop.makePromise(of: Void.self) - promise.futureResult.whenComplete { result in - self.state.withLockedValue { state in - switch result { - case .success: - state.mayReceiveData = true - case .failure: - state.expectError = true - } - } - } - - self.didReceiveHead(head, promise) - return promise.futureResult - } - - func didReceiveBodyPart(task: HTTPClient.Task, _ buffer: ByteBuffer) -> EventLoopFuture { - self.state.withLockedValue { - XCTAssertTrue($0.mayReceiveData) - XCTAssertFalse($0.expectError) - $0.data.writeImmutableBuffer(buffer) - } - return self.eventLoop.makeSucceededFuture(()) - } - - func didFinishRequest(task: HTTPClient.Task) throws -> Response { - self.state.withLockedValue { - XCTAssertTrue($0.mayReceiveData) - XCTAssertFalse($0.expectError) - return $0.data - } - } - - func didReceiveError(task: HTTPClient.Task, _ error: Error) { - self.state.withLockedValue { - XCTAssertTrue($0.expectError) - } - } -} - -enum TemporaryFileHelpers { - private static var temporaryDirectory: String { - #if targetEnvironment(simulator) - // Simulator temp directories are so long (and contain the user name) that they're not usable - // for UNIX Domain Socket paths (which are limited to 103 bytes). - return "/tmp" - #else - #if os(Android) - return "/data/local/tmp" - #elseif os(Linux) - return "/tmp" - #else - if #available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *) { - return FileManager.default.temporaryDirectory.path - } else { - return "/tmp" - } - #endif // os - #endif // targetEnvironment - } - - private static func openTemporaryFile() -> (CInt, String) { - let template = "\(temporaryDirectory)/ahc_XXXXXX" - var templateBytes = template.utf8 + [0] - let templateBytesCount = templateBytes.count - let fd = templateBytes.withUnsafeMutableBufferPointer { ptr in - ptr.baseAddress!.withMemoryRebound(to: Int8.self, capacity: templateBytesCount) { ptr in - mkstemp(ptr) - } - } - templateBytes.removeLast() - return (fd, String(decoding: templateBytes, as: Unicode.UTF8.self)) - } - - /// This function creates a filename that can be used for a temporary UNIX domain socket path. - /// - /// If the temporary directory is too long to store a UNIX domain socket path, it will `chdir` into the temporary - /// directory and return a short-enough path. The iOS simulator is known to have too long paths. - internal static func withTemporaryUnixDomainSocketPathName( - directory: String = temporaryDirectory, - _ body: (String) throws -> T - ) throws -> T { - // this is racy but we're trying to create the shortest possible path so we can't add a directory... - let (fd, path) = self.openTemporaryFile() - close(fd) - try! FileManager.default.removeItem(atPath: path) - - let saveCurrentDirectory = FileManager.default.currentDirectoryPath - let restoreSavedCWD: Bool - let shortEnoughPath: String - do { - _ = try SocketAddress(unixDomainSocketPath: path) - // this seems to be short enough for a UDS - shortEnoughPath = path - restoreSavedCWD = false - } catch SocketAddressError.unixDomainSocketPathTooLong { - _ = FileManager.default.changeCurrentDirectoryPath( - URL(fileURLWithPath: path).deletingLastPathComponent().absoluteString - ) - shortEnoughPath = URL(fileURLWithPath: path).lastPathComponent - restoreSavedCWD = true - print( - "WARNING: Path '\(path)' could not be used as UNIX domain socket path, using chdir & '\(shortEnoughPath)'" - ) - } - defer { - if FileManager.default.fileExists(atPath: path) { - try? FileManager.default.removeItem(atPath: path) - } - if restoreSavedCWD { - _ = FileManager.default.changeCurrentDirectoryPath(saveCurrentDirectory) - } - } - return try body(shortEnoughPath) - } - - /// This function creates a filename that can be used as a temporary file. - internal static func withTemporaryFilePath( - directory: String = temporaryDirectory, - _ body: (String) throws -> T - ) throws -> T { - let (fd, path) = self.openTemporaryFile() - close(fd) - try! FileManager.default.removeItem(atPath: path) - - defer { - if FileManager.default.fileExists(atPath: path) { - try? FileManager.default.removeItem(atPath: path) - } - } - return try body(path) - } - - internal static func makeTemporaryFilePath( - directory: String = temporaryDirectory - ) -> String { - let (fd, path) = self.openTemporaryFile() - close(fd) - try! FileManager.default.removeItem(atPath: path) - return path - } - - internal static func removeTemporaryFile( - at path: String - ) { - if FileManager.default.fileExists(atPath: path) { - try? FileManager.default.removeItem(atPath: path) - } - } - - internal static func fileSize(path: String) throws -> Int? { - try FileManager.default.attributesOfItem(atPath: path)[.size] as? Int - } - - internal static func fileExists(path: String) -> Bool { - FileManager.default.fileExists(atPath: path) - } -} - -enum TestTLS { - static let certificate = try! NIOSSLCertificate(bytes: Array(cert.utf8), format: .pem) - static let privateKey = try! NIOSSLPrivateKey(bytes: Array(key.utf8), format: .pem) - static let serverConfiguration: TLSConfiguration = .makeServerConfiguration( - certificateChain: [.certificate(TestTLS.certificate)], - privateKey: .privateKey(TestTLS.privateKey) - ) -} - -internal final class HTTPBin: Sendable -where - RequestHandler.InboundIn == HTTPServerRequestPart, - RequestHandler.OutboundOut == HTTPServerResponsePart -{ - enum BindTarget { - case unixDomainSocket(String) - case localhostIPv4RandomPort - case localhostIPv6RandomPort - } - - enum Mode { - // refuses all connections - case refuse - // supports http1.1 connections only, which can be either plain text or encrypted - case http1_1( - tlsConfiguration: TLSConfiguration? = nil, - compress: Bool = false - ) - // supports http1.1 and http2 connections which must be always encrypted - case http2( - tlsConfiguration: TLSConfiguration = TestTLS.serverConfiguration, - compress: Bool = false, - settings: HTTP2Settings? = nil - ) - - static func http1_1(ssl: Bool, compress: Bool = false) -> Self { - .http1_1(tlsConfiguration: ssl ? TestTLS.serverConfiguration : nil, compress: compress) - } - - // supports request decompression and http response compression - var compress: Bool { - switch self { - case .refuse: - return false - case .http1_1(_, let compress), .http2(_, let compress, _): - return compress - } - } - - var httpSettings: HTTP2Settings { - switch self { - case .http1_1, .http2(_, _, nil), .refuse: - return HTTP2Connection.defaultSettings - case .http2(_, _, .some(let customSettings)): - return customSettings - } - } - - var tlsConfiguration: TLSConfiguration? { - switch self { - case .refuse: - return nil - case .http1_1(let tlsConfiguration, _): - return tlsConfiguration - case .http2(var tlsConfiguration, _, _): - tlsConfiguration.applicationProtocols = NIOHTTP2SupportedALPNProtocols - return tlsConfiguration - } - } - } - - enum Proxy { - case none - case simulate(authorization: String?) - } - - let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) - - private let activeConnCounterHandler: ConnectionsCountHandler - var activeConnections: Int { - self.activeConnCounterHandler.currentlyActiveConnections - } - - var createdConnections: Int { - self.activeConnCounterHandler.createdConnections - } - - var port: Int { - self.serverChannel.withLockedValue { - Int($0!.localAddress!.port!) - } - } - - var socketAddress: SocketAddress { - self.serverChannel.withLockedValue { - $0!.localAddress! - } - } - - var baseURL: String { - let scheme: String = { - switch mode { - case .http1_1, .refuse: - return "http" - case .http2: - return "https" - } - }() - let host: String = { - switch self.socketAddress { - case .v4: - return self.socketAddress.ipAddress! - case .v6: - return "[\(self.socketAddress.ipAddress!)]" - case .unixDomainSocket: - return self.socketAddress.pathname! - } - }() - - return "\(scheme)://\(host):\(self.port)/" - } - - private let mode: Mode - private let sslContext: NIOSSLContext? - private let serverChannel = NIOLockedValueBox(nil) - private let isShutdown = ManagedAtomic(false) - private let handlerFactory: @Sendable (Int) -> (RequestHandler) - - init( - _ mode: Mode = .http1_1(ssl: false, compress: false), - proxy: Proxy = .none, - bindTarget: BindTarget = .localhostIPv4RandomPort, - reusePort: Bool = false, - trafficShapingTargetBytesPerSecond: Int? = nil, - handlerFactory: @escaping @Sendable (Int) -> (RequestHandler) - ) { - self.mode = mode - self.sslContext = HTTPBin.sslContext(for: mode) - self.handlerFactory = handlerFactory - - let socketAddress: SocketAddress - switch bindTarget { - case .localhostIPv4RandomPort: - socketAddress = try! SocketAddress(ipAddress: "127.0.0.1", port: 0) - case .localhostIPv6RandomPort: - socketAddress = try! SocketAddress(ipAddress: "::1", port: 0) - case .unixDomainSocket(let path): - socketAddress = try! SocketAddress(unixDomainSocketPath: path) - } - - self.activeConnCounterHandler = ConnectionsCountHandler() - - let connectionIDAtomic = ManagedAtomic(0) - - let serverChannel = try! ServerBootstrap(group: self.group) - .serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) - .serverChannelOption( - ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEPORT), - value: reusePort ? 1 : 0 - ) - .serverChannelInitializer { [activeConnCounterHandler] channel in - channel.pipeline.addHandler(activeConnCounterHandler) - }.childChannelInitializer { channel in - if let trafficShapingTargetBytesPerSecond = trafficShapingTargetBytesPerSecond { - try! channel.pipeline.syncOperations.addHandler( - BasicInboundTrafficShapingHandler( - targetBytesPerSecond: trafficShapingTargetBytesPerSecond - ) - ) - } - do { - let connectionID = connectionIDAtomic.loadThenWrappingIncrement(ordering: .relaxed) - - if case .refuse = mode { - throw HTTPBinError.refusedConnection - } - - // if we need to simulate a proxy, we need to add those handlers first - if case .simulate(authorization: let expectedAuthorization) = proxy { - try self.syncAddHTTPProxyHandlers( - to: channel, - connectionID: connectionID, - expectedAuthorization: expectedAuthorization - ) - return channel.eventLoop.makeSucceededVoidFuture() - } - - // if a connection has been established, we need to negotiate TLS before - // anything else. Depending on the negotiation, the HTTPHandlers will be added. - if let sslContext = self.sslContext { - try self.addTLSHandlerAndUpgrader( - to: channel, - sslContext: sslContext, - connectionID: connectionID - ) - return channel.eventLoop.makeSucceededVoidFuture() - } - - // if neither HTTP Proxy nor TLS are wanted, we can add HTTP1 handlers directly - try self.syncAddHTTP1Handlers(to: channel, connectionID: connectionID) - return channel.eventLoop.makeSucceededVoidFuture() - } catch { - return channel.eventLoop.makeFailedFuture(error) - } - }.bind(to: socketAddress).wait() - self.serverChannel.withLockedValue { $0 = serverChannel } - } - - private func syncAddHTTPProxyHandlers( - to channel: Channel, - connectionID: Int, - expectedAuthorization: String? - ) throws { - let sync = channel.pipeline.syncOperations - let promise = channel.eventLoop.makePromise(of: Void.self) - - let responseEncoder = HTTPResponseEncoder() - let requestDecoder = ByteToMessageHandler(HTTPRequestDecoder(leftOverBytesStrategy: .forwardBytes)) - let proxySimulator = HTTPProxySimulator(promise: promise, expectedAuthorization: expectedAuthorization) - - try sync.addHandler(responseEncoder) - try sync.addHandler(requestDecoder) - try sync.addHandler(proxySimulator) - - promise.futureResult.assumeIsolated().flatMap { _ in - channel.pipeline.syncOperations.removeHandler(proxySimulator) - }.flatMap { _ in - channel.pipeline.syncOperations.removeHandler(responseEncoder) - }.flatMap { _ in - channel.pipeline.syncOperations.removeHandler(requestDecoder) - }.whenComplete { result in - switch result { - case .failure: - channel.close(mode: .all, promise: nil) - case .success: - self.httpProxyEstablished(channel, connectionID: connectionID) - } - } - } - - func syncAddHTTP1Handlers(to channel: Channel, connectionID: Int) throws { - let sync = channel.pipeline.syncOperations - try sync.configureHTTPServerPipeline(withPipeliningAssistance: true, withErrorHandling: true) - - if self.mode.compress { - try sync.addHandler(HTTPResponseCompressor()) - } - - try sync.addHandler(self.handlerFactory(connectionID)) - } - - private func httpProxyEstablished(_ channel: Channel, connectionID: Int) { - do { - // if a connection has been established, we need to negotiate TLS before - // anything else. Depending on the negotiation, the HTTPHandlers will be added. - if let sslContext = self.sslContext { - try self.addTLSHandlerAndUpgrader( - to: channel, - sslContext: sslContext, - connectionID: connectionID - ) - return - } - - // if neither HTTP Proxy nor TLS are wanted, we can add HTTP1 handlers directly - try self.syncAddHTTP1Handlers(to: channel, connectionID: connectionID) - } catch { - // in case of an while modifying the pipeline we should close the connection - channel.close(mode: .all, promise: nil) - } - } - - private static func sslContext(for mode: Mode) -> NIOSSLContext? { - if let tlsConfiguration = mode.tlsConfiguration { - return try! NIOSSLContext(configuration: tlsConfiguration) - } - return nil - } - - private func addTLSHandlerAndUpgrader(to channel: Channel, sslContext: NIOSSLContext, connectionID: Int) throws { - let sslHandler = NIOSSLServerHandler(context: sslContext) - - // copy pasted from NIOHTTP2 - let alpnHandler = ApplicationProtocolNegotiationHandler { result in - do { - switch result { - case .negotiated("h2"): - // Successful upgrade to HTTP/2. Let the user configure the pipeline. - let http2Handler = NIOHTTP2Handler( - mode: .server, - initialSettings: self.mode.httpSettings - ) - let multiplexer = HTTP2StreamMultiplexer( - mode: .server, - channel: channel, - targetWindowSize: 16 * 1024 * 1024, // 16 MiB - inboundStreamInitializer: { channel in - do { - let sync = channel.pipeline.syncOperations - - try sync.addHandler(HTTP2FramePayloadToHTTP1ServerCodec()) - if self.mode.compress { - try sync.addHandler(HTTPResponseCompressor()) - } - try sync.addHandler(self.handlerFactory(connectionID)) - - return channel.eventLoop.makeSucceededVoidFuture() - } catch { - return channel.eventLoop.makeFailedFuture(error) - } - } - ) - - let sync = channel.pipeline.syncOperations - - try sync.addHandler(http2Handler) - try sync.addHandler(multiplexer) - case .negotiated("http/1.1"), .fallback: - // Explicit or implicit HTTP/1.1 choice. - try self.syncAddHTTP1Handlers(to: channel, connectionID: connectionID) - case .negotiated: - // We negotiated something that isn't HTTP/1.1. This is a bad scene, and is a good indication - // of a user configuration error. We're going to close the connection directly. - channel.close(mode: .all, promise: nil) - throw NIOHTTP2Errors.invalidALPNToken() - } - - return channel.eventLoop.makeSucceededVoidFuture() - } catch { - return channel.eventLoop.makeFailedFuture(error) - } - } - - try channel.pipeline.syncOperations.addHandler(sslHandler) - try channel.pipeline.syncOperations.addHandler(alpnHandler) - } - - func shutdown() throws { - self.isShutdown.store(true, ordering: .relaxed) - try self.group.syncShutdownGracefully() - } - - deinit { - assert(self.isShutdown.load(ordering: .relaxed), "HTTPBin not shutdown before deinit") - } -} - -extension HTTPBin where RequestHandler == HTTPBinHandler { - convenience init( - _ mode: Mode = .http1_1(ssl: false, compress: false), - proxy: Proxy = .none, - bindTarget: BindTarget = .localhostIPv4RandomPort, - reusePort: Bool = false, - trafficShapingTargetBytesPerSecond: Int? = nil - ) { - self.init( - mode, - proxy: proxy, - bindTarget: bindTarget, - reusePort: reusePort, - trafficShapingTargetBytesPerSecond: trafficShapingTargetBytesPerSecond - ) { HTTPBinHandler(connectionID: $0) } - } -} - -enum HTTPBinError: Error { - case refusedConnection - case invalidProxyRequest -} - -final class HTTPProxySimulator: ChannelInboundHandler, RemovableChannelHandler { - typealias InboundIn = HTTPServerRequestPart - typealias InboundOut = HTTPServerResponsePart - typealias OutboundOut = HTTPServerResponsePart - - // the promise to succeed, once the proxy connection is setup - let promise: EventLoopPromise - let expectedAuthorization: String? - - var head: HTTPResponseHead - - init(promise: EventLoopPromise, expectedAuthorization: String?) { - self.promise = promise - self.expectedAuthorization = expectedAuthorization - self.head = HTTPResponseHead( - version: .init(major: 1, minor: 1), - status: .ok, - headers: .init([("Content-Length", "0")]) - ) - } - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - let request = self.unwrapInboundIn(data) - switch request { - case .head(let head): - guard head.method == .CONNECT else { - self.head.status = .badRequest - return - } - - if let expectedAuthorization = self.expectedAuthorization { - guard let authorization = head.headers["proxy-authorization"].first, - expectedAuthorization == authorization - else { - self.head.status = .proxyAuthenticationRequired - return - } - } - - case .body: - () - case .end: - let okay = self.head.status != .ok - context.write(self.wrapOutboundOut(.head(self.head)), promise: nil) - context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: nil) - if okay { - self.promise.fail(HTTPBinError.invalidProxyRequest) - } else { - self.promise.succeed(()) - } - } - } -} - -internal struct HTTPResponseBuilder { - var head: HTTPResponseHead - var body: ByteBuffer? - var requestBodyByteCount: Int - let responseBodyIsRequestBodyByteCount: Bool - - init( - _ version: HTTPVersion = HTTPVersion(major: 1, minor: 1), - status: HTTPResponseStatus, - headers: HTTPHeaders = HTTPHeaders(), - responseBodyIsRequestBodyByteCount: Bool = false - ) { - self.head = HTTPResponseHead(version: version, status: status, headers: headers) - self.requestBodyByteCount = 0 - self.responseBodyIsRequestBodyByteCount = responseBodyIsRequestBodyByteCount - } - - mutating func add(_ part: ByteBuffer) { - self.requestBodyByteCount += part.readableBytes - guard !self.responseBodyIsRequestBodyByteCount else { - if self.body == nil { - self.body = ByteBuffer() - self.body!.reserveCapacity(100) - } - self.body!.clear() - self.body!.writeString("\(self.requestBodyByteCount)") - return - } - if var body = body { - var part = part - body.writeBuffer(&part) - self.body = body - } else { - self.body = part - } - } -} - -internal struct RequestInfo: Codable, Equatable { - var data: String - var requestNumber: Int - var connectionNumber: Int -} - -internal final class HTTPBinHandler: ChannelInboundHandler { - typealias InboundIn = HTTPServerRequestPart - typealias OutboundOut = HTTPServerResponsePart - - var resps = CircularBuffer() - var responseHeaders = HTTPHeaders() - var delay: TimeAmount = .seconds(0) - let creationDate = Date() - var shouldClose = false - var isServingRequest = false - let connectionID: Int - var requestId: Int = 0 - - init(connectionID: Int) { - self.connectionID = connectionID - } - - func parseAndSetOptions(from head: HTTPRequestHead) { - if let delay = head.headers["X-internal-delay"].first { - if let milliseconds = Int64(delay) { - self.delay = TimeAmount.milliseconds(milliseconds) - } else { - assertionFailure("Invalid interval format") - } - } else { - self.delay = .nanoseconds(0) - } - - for header in head.headers { - let needle = "x-send-back-header-" - if header.name.lowercased().starts(with: needle) { - self.responseHeaders.add( - name: String(header.name.dropFirst(needle.count)), - value: header.value - ) - } - } - } - - func writeEvents(context: ChannelHandlerContext, isContentLengthRequired: Bool = false) { - let headers: HTTPHeaders - if isContentLengthRequired { - headers = HTTPHeaders([("Content-Length", "50")]) - } else { - headers = HTTPHeaders() - } - - context.write( - wrapOutboundOut( - .head(HTTPResponseHead(version: HTTPVersion(major: 1, minor: 1), status: .ok, headers: headers)) - ), - promise: nil - ) - for i in 0..<10 { - let msg = "id: \(i)" - var buf = context.channel.allocator.buffer(capacity: msg.count) - buf.writeString(msg) - context.writeAndFlush(wrapOutboundOut(.body(.byteBuffer(buf))), promise: nil) - Thread.sleep(forTimeInterval: 0.05) - } - context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: nil) - } - - func writeChunked(context: ChannelHandlerContext) { - // This tests receiving chunks very fast: please do not insert delays here! - let headers = HTTPHeaders([("Transfer-Encoding", "chunked")]) - - context.write( - self.wrapOutboundOut( - .head(HTTPResponseHead(version: HTTPVersion(major: 1, minor: 1), status: .ok, headers: headers)) - ), - promise: nil - ) - for i in 0..<10 { - let msg = "id: \(i)" - var buf = context.channel.allocator.buffer(capacity: msg.count) - buf.writeString(msg) - context.write(wrapOutboundOut(.body(.byteBuffer(buf))), promise: nil) - } - - context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: nil) - } - - func writeManyChunks(context: ChannelHandlerContext) { - // This tests receiving a lot of tiny chunks: they must all be sent in a single flush or the test doesn't work. - let headers = HTTPHeaders([("Transfer-Encoding", "chunked")]) - - context.write( - self.wrapOutboundOut( - .head(HTTPResponseHead(version: HTTPVersion(major: 1, minor: 1), status: .ok, headers: headers)) - ), - promise: nil - ) - let message = ByteBuffer(integer: UInt8(ascii: "a")) - - // This number (10k) is load-bearing and a bit magic: it has been experimentally verified as being sufficient to blow the stack - // in the old implementation on all testing platforms. Please don't change it without good reason. - for _ in 0..<10_000 { - context.write(wrapOutboundOut(.body(.byteBuffer(message))), promise: nil) - } - - context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: nil) - } - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - self.isServingRequest = true - switch self.unwrapInboundIn(data) { - case .head(let req): - self.responseHeaders = HTTPHeaders() - self.requestId += 1 - self.parseAndSetOptions(from: req) - let urlComponents = URLComponents(string: req.uri)! - switch urlComponents.percentEncodedPath { - case "/": - var headers = self.responseHeaders - headers.add(name: "X-Is-This-Slash", value: "Yes") - self.resps.append(HTTPResponseBuilder(status: .ok, headers: headers)) - return - case "/echo-uri": - var headers = self.responseHeaders - headers.add(name: "X-Calling-URI", value: req.uri) - self.resps.append(HTTPResponseBuilder(status: .ok, headers: headers)) - return - case "/echo-method": - var headers = self.responseHeaders - headers.add(name: "X-Method-Used", value: req.method.rawValue) - self.resps.append(HTTPResponseBuilder(status: .ok, headers: headers)) - return - case "/ok": - self.resps.append(HTTPResponseBuilder(status: .ok)) - return - case "/get": - if req.method != .GET { - self.resps.append(HTTPResponseBuilder(status: .methodNotAllowed)) - return - } - self.resps.append(HTTPResponseBuilder(status: .ok)) - return - case "/stats": - var body = context.channel.allocator.buffer(capacity: 1) - body.writeString("Just some stats mate.") - var builder = HTTPResponseBuilder(status: .ok) - builder.add(body) - - self.resps.append(builder) - case "/post": - if req.method != .POST { - self.resps.append(HTTPResponseBuilder(status: .methodNotAllowed)) - return - } - self.resps.append(HTTPResponseBuilder(status: .ok)) - return - case "/post-respond-with-byte-count": - if req.method != .POST { - self.resps.append(HTTPResponseBuilder(status: .methodNotAllowed)) - return - } - self.resps.append(HTTPResponseBuilder(status: .ok, responseBodyIsRequestBodyByteCount: true)) - return - case "/redirect/302": - var headers = self.responseHeaders - headers.add(name: "location", value: "/ok") - self.resps.append(HTTPResponseBuilder(status: .found, headers: headers)) - return - case "/redirect/https": - let port = self.value(for: "port", from: urlComponents.query!) - var headers = self.responseHeaders - headers.add(name: "Location", value: "https://localhost/(port)/ok") - self.resps.append(HTTPResponseBuilder(status: .found, headers: headers)) - return - case "/redirect/loopback": - let port = self.value(for: "port", from: urlComponents.query!) - var headers = self.responseHeaders - headers.add(name: "Location", value: "http://127.0.0.1/(port)/echohostheader") - self.resps.append(HTTPResponseBuilder(status: .found, headers: headers)) - return - case "/redirect/infinite1": - var headers = self.responseHeaders - headers.add(name: "Location", value: "/redirect/infinite2") - self.resps.append(HTTPResponseBuilder(status: .found, headers: headers)) - return - case "/redirect/infinite2": - var headers = self.responseHeaders - headers.add(name: "Location", value: "/redirect/infinite1") - self.resps.append(HTTPResponseBuilder(status: .found, headers: headers)) - return - case "/redirect/target": - var headers = self.responseHeaders - let targetURL = req.headers["X-Target-Redirect-URL"].first ?? "" - headers.add(name: "Location", value: targetURL) - self.resps.append(HTTPResponseBuilder(status: .found, headers: headers)) - return - case "/percent%20encoded": - if req.method != .GET { - self.resps.append(HTTPResponseBuilder(status: .methodNotAllowed)) - return - } - self.resps.append(HTTPResponseBuilder(status: .ok)) - return - case "/percent%2Fencoded/hello": - if req.method != .GET { - self.resps.append(HTTPResponseBuilder(status: .methodNotAllowed)) - return - } - self.resps.append(HTTPResponseBuilder(status: .ok)) - return - case "/echohostheader": - var builder = HTTPResponseBuilder(status: .ok) - let hostValue = req.headers["Host"].first ?? "" - let buff = context.channel.allocator.buffer(string: hostValue) - builder.add(buff) - self.resps.append(builder) - return - case "/wait": - return - case "/close": - context.close(promise: nil) - return - case "/custom": - context.writeAndFlush( - wrapOutboundOut(.head(HTTPResponseHead(version: HTTPVersion(major: 1, minor: 1), status: .ok))), - promise: nil - ) - return - case "/events/10/1": // TODO: parse path - self.writeEvents(context: context) - return - case "/events/10/content-length": - self.writeEvents(context: context, isContentLengthRequired: true) - case "/chunked": - self.writeChunked(context: context) - return - case "/mega-chunked": - self.writeManyChunks(context: context) - return - case "/close-on-response": - var headers = self.responseHeaders - headers.replaceOrAdd(name: "connection", value: "close") - - var builder = HTTPResponseBuilder(.http1_1, status: .ok, headers: headers) - builder.body = ByteBuffer(string: "some body content") - - // We're forcing this closed now. - self.shouldClose = true - self.resps.append(builder) - case "/content-length-without-body": - var headers = self.responseHeaders - headers.replaceOrAdd(name: "content-length", value: "1234") - context.writeAndFlush( - wrapOutboundOut( - .head(HTTPResponseHead(version: HTTPVersion(major: 1, minor: 1), status: .ok, headers: headers)) - ), - promise: nil - ) - return - default: - context.write( - wrapOutboundOut( - .head(HTTPResponseHead(version: HTTPVersion(major: 1, minor: 1), status: .notFound)) - ), - promise: nil - ) - context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: nil) - return - } - case .body(let body): - if self.resps.isEmpty { - return - } - var response = self.resps.removeFirst() - response.add(body) - self.resps.prepend(response) - case .end: - if self.resps.isEmpty { - return - } - var response = self.resps.removeFirst() - response.head.headers.add(contentsOf: self.responseHeaders) - context.write(wrapOutboundOut(.head(response.head)), promise: nil) - if let body = response.body { - let requestInfo = RequestInfo( - data: String(buffer: body), - requestNumber: self.requestId, - connectionNumber: self.connectionID - ) - let responseBody = try! JSONEncoder().encodeAsByteBuffer( - requestInfo, - allocator: context.channel.allocator - ) - context.write(wrapOutboundOut(.body(.byteBuffer(responseBody))), promise: nil) - } else { - let requestInfo = RequestInfo( - data: "", - requestNumber: self.requestId, - connectionNumber: self.connectionID - ) - let responseBody = try! JSONEncoder().encodeAsByteBuffer( - requestInfo, - allocator: context.channel.allocator - ) - context.write(wrapOutboundOut(.body(.byteBuffer(responseBody))), promise: nil) - } - context.eventLoop.assumeIsolated().scheduleTask(in: self.delay) { - guard context.channel.isActive else { - context.close(promise: nil) - return - } - - context.writeAndFlush(self.wrapOutboundOut(.end(nil))).assumeIsolated().whenComplete { result in - self.isServingRequest = false - switch result { - case .success: - if self.responseHeaders[canonicalForm: "X-Close-Connection"].contains("true") - || self.shouldClose - { - context.close(promise: nil) - } - case .failure(let error): - assertionFailure("\(error)") - } - } - } - } - } - - func value(for key: String, from query: String) -> String { - let components = query.components(separatedBy: "&").map { - $0.trimmingCharacters(in: .whitespaces) - } - - for component in components { - let nameAndValue = component.components(separatedBy: "=").map { - $0.trimmingCharacters(in: .whitespaces) - } - - if nameAndValue[0] == key { - return nameAndValue[1] - } - } - fatalError("parameter \(key) is missing from query: \(query)") - } -} - -final class ConnectionsCountHandler: ChannelInboundHandler, Sendable { - typealias InboundIn = Channel - - private let activeConns = ManagedAtomic(0) - private let createdConns = ManagedAtomic(0) - - var createdConnections: Int { - self.createdConns.load(ordering: .relaxed) - } - - var currentlyActiveConnections: Int { - self.activeConns.load(ordering: .relaxed) - } - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - let channel = self.unwrapInboundIn(data) - - _ = self.activeConns.loadThenWrappingIncrement(ordering: .relaxed) - _ = self.createdConns.loadThenWrappingIncrement(ordering: .relaxed) - channel.closeFuture.whenComplete { [activeConns] _ in - _ = activeConns.loadThenWrappingDecrement(ordering: .relaxed) - } - - context.fireChannelRead(data) - } -} - -internal final class CloseWithoutClosingServerHandler: ChannelInboundHandler { - typealias InboundIn = HTTPServerRequestPart - typealias OutboundOut = HTTPServerResponsePart - - private var callback: (() -> Void)? - private var onClosePromise: EventLoopPromise? - - init(_ callback: @escaping () -> Void) { - self.callback = callback - } - - func handlerAdded(context: ChannelHandlerContext) { - self.onClosePromise = context.eventLoop.makePromise() - self.onClosePromise!.futureResult.assumeIsolated().whenSuccess(self.callback!) - self.callback = nil - } - - func handlerRemoved(context: ChannelHandlerContext) { - assert(self.onClosePromise == nil) - } - - func channelInactive(context: ChannelHandlerContext) { - if let onClosePromise = self.onClosePromise { - self.onClosePromise = nil - onClosePromise.succeed(()) - } - } - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - guard case .end = self.unwrapInboundIn(data) else { - return - } - - // We're gonna send a response back here, with Connection: close, but we will - // not close the connection. This reproduces #324. - let headers = HTTPHeaders([ - ("Host", "CloseWithoutClosingServerHandler"), - ("Content-Length", "0"), - ("Connection", "close"), - ]) - let head = HTTPResponseHead(version: .init(major: 1, minor: 1), status: .ok, headers: headers) - - context.write(self.wrapOutboundOut(.head(head)), promise: nil) - context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: nil) - } -} - -final class ExpectClosureServerHandler: ChannelInboundHandler { - typealias InboundIn = HTTPServerRequestPart - typealias OutboundOut = HTTPServerResponsePart - - private let onClosePromise: EventLoopPromise - - init(onClosePromise: EventLoopPromise) { - self.onClosePromise = onClosePromise - } - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - switch self.unwrapInboundIn(data) { - case .head: - let head = HTTPResponseHead(version: .http1_1, status: .ok, headers: ["Content-Length": "0"]) - context.write(self.wrapOutboundOut(.head(head)), promise: nil) - context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: nil) - case .body, .end: - () - } - } - - func channelInactive(context: ChannelHandlerContext) { - self.onClosePromise.succeed(()) - } -} - -struct EventLoopFutureTimeoutError: Error {} - -extension EventLoopFuture where Value: Sendable { - func timeout(after failDelay: TimeAmount) -> EventLoopFuture { - let promise = self.eventLoop.makePromise(of: Value.self) - - self.whenComplete { result in - switch result { - case .success(let value): - promise.succeed(value) - case .failure(let error): - promise.fail(error) - } - } - - self.eventLoop.scheduleTask(in: failDelay) { - promise.fail(EventLoopFutureTimeoutError()) - } - - return promise.futureResult - } -} - -struct CollectEverythingLogHandler: LogHandler { - var metadata: Logger.Metadata = [:] - var logLevel: Logger.Level = .info - let logStore: LogStore - - final class LogStore: Sendable { - struct Entry { - var level: Logger.Level - var message: String - var metadata: [String: String] - } - - private let logs = NIOLockedValueBox<[Entry]>([]) - - var allEntries: [Entry] { - get { - self.logs.withLockedValue { $0 } - } - set { - self.logs.withLockedValue { $0 = newValue } - } - } - - func append(level: Logger.Level, message: Logger.Message, metadata: Logger.Metadata?) { - self.logs.withLockedValue { - $0.append( - Entry( - level: level, - message: message.description, - metadata: metadata?.mapValues { $0.description } ?? [:] - ) - ) - } - } - } - - init(logStore: LogStore) { - self.logStore = logStore - } - - func log( - level: Logger.Level, - message: Logger.Message, - metadata: Logger.Metadata?, - source: String, - file: String, - function: String, - line: UInt - ) { - self.logStore.append(level: level, message: message, metadata: self.metadata.merging(metadata ?? [:]) { $1 }) - } - - subscript(metadataKey key: String) -> Logger.Metadata.Value? { - get { - self.metadata[key] - } - set { - self.metadata[key] = newValue - } - } -} - -/// A ``HTTPClientResponseDelegate`` that buffers the incoming response parts for the consumer. The consumer can -/// consume the bytes by calling ``next()`` on the delegate. -/// -/// The sole purpose of this class is to enable straight-line stream tests. -final class ResponseStreamDelegate: HTTPClientResponseDelegate { - typealias Response = Void - - enum State: Sendable { - /// The delegate is in the idle state. There are no http response parts to be buffered - /// and the consumer did not signal a demand. Transitions to all other states are allowed. - case idle - /// The consumer has signaled a demand for more bytes, but none where available. Can - /// transition to `.idle` (when new bytes arrive), `.finished` (when the stream finishes or fails) - case waitingForBytes(EventLoopPromise) - /// The consumer has signaled no further demand but bytes keep arriving. Valid transitions - /// to `.idle` (when bytes are consumed), `.finished` (when bytes are consumed, and the - /// stream has ended), `.failed` (if an error is forwarded) - case buffering(ByteBuffer, done: Bool) - /// Stores an error for consumption. Valid transitions are: `.finished`, when the error was consumed. - case failed(Error) - /// The stream has finished and all bytes or errors where consumed. - case finished - } - - let eventLoop: EventLoop - private let state: NIOLoopBoundBox - - init(eventLoop: EventLoop) { - self.eventLoop = eventLoop - self.state = .makeBoxSendingValue(.idle, eventLoop: eventLoop) - } - - func next() -> EventLoopFuture { - if self.eventLoop.inEventLoop { - return self.next0() - } else { - return self.eventLoop.flatSubmit { - self.next0() - } - } - } - - private func next0() -> EventLoopFuture { - switch self.state.value { - case .idle: - let promise = self.eventLoop.makePromise(of: ByteBuffer?.self) - self.state.value = .waitingForBytes(promise) - return promise.futureResult - - case .buffering(let byteBuffer, done: false): - self.state.value = .idle - return self.eventLoop.makeSucceededFuture(byteBuffer) - - case .buffering(let byteBuffer, done: true): - self.state.value = .finished - return self.eventLoop.makeSucceededFuture(byteBuffer) - - case .waitingForBytes: - preconditionFailure("Don't call `.next` twice") - - case .failed(let error): - self.state.value = .finished - return self.eventLoop.makeFailedFuture(error) - - case .finished: - return self.eventLoop.makeSucceededFuture(nil) - } - } - - // MARK: HTTPClientResponseDelegate - - func didSendRequestHead(task: HTTPClient.Task, _ head: HTTPRequestHead) { - self.eventLoop.preconditionInEventLoop() - } - - func didSendRequestPart(task: HTTPClient.Task, _ part: IOData) { - self.eventLoop.preconditionInEventLoop() - } - - func didSendRequest(task: HTTPClient.Task) { - self.eventLoop.preconditionInEventLoop() - } - - func didReceiveHead(task: HTTPClient.Task, _ head: HTTPResponseHead) -> EventLoopFuture { - self.eventLoop.preconditionInEventLoop() - return task.eventLoop.makeSucceededVoidFuture() - } - - func didReceiveBodyPart(task: HTTPClient.Task, _ buffer: ByteBuffer) -> EventLoopFuture { - self.eventLoop.preconditionInEventLoop() - - switch self.state.value { - case .idle: - self.state.value = .buffering(buffer, done: false) - case .waitingForBytes(let promise): - self.state.value = .idle - promise.succeed(buffer) - case .buffering(var byteBuffer, done: false): - var buffer = buffer - byteBuffer.writeBuffer(&buffer) - self.state.value = .buffering(byteBuffer, done: false) - case .buffering(_, done: true), .finished, .failed: - preconditionFailure("Invalid state: \(self.state)") - } - - return task.eventLoop.makeSucceededVoidFuture() - } - - func didReceiveError(task: HTTPClient.Task, _ error: Error) { - self.eventLoop.preconditionInEventLoop() - - switch self.state.value { - case .idle: - self.state.value = .failed(error) - case .waitingForBytes(let promise): - self.state.value = .finished - promise.fail(error) - case .buffering(_, done: false): - self.state.value = .failed(error) - case .buffering(_, done: true), .finished, .failed: - preconditionFailure("Invalid state: \(self.state)") - } - } - - func didFinishRequest(task: HTTPClient.Task) throws { - self.eventLoop.preconditionInEventLoop() - - switch self.state.value { - case .idle: - self.state.value = .finished - case .waitingForBytes(let promise): - self.state.value = .finished - promise.succeed(nil) - case .buffering(let byteBuffer, done: false): - self.state.value = .buffering(byteBuffer, done: true) - case .buffering(_, done: true), .finished, .failed: - preconditionFailure("Invalid state: \(self.state)") - } - } -} - -class HTTPEchoHandler: ChannelInboundHandler { - typealias InboundIn = HTTPServerRequestPart - typealias OutboundOut = HTTPServerResponsePart - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - let request = self.unwrapInboundIn(data) - switch request { - case .head(let requestHead): - context.writeAndFlush( - self.wrapOutboundOut(.head(.init(version: .http1_1, status: .ok, headers: requestHead.headers))), - promise: nil - ) - case .body(let bytes): - context.writeAndFlush(self.wrapOutboundOut(.body(.byteBuffer(bytes))), promise: nil) - case .end: - context.writeAndFlush(self.wrapOutboundOut(.end(nil))).assumeIsolated().whenSuccess { - context.close(promise: nil) - } - } - } -} - -final class HTTPEchoHeaders: ChannelInboundHandler { - typealias InboundIn = HTTPServerRequestPart - typealias OutboundOut = HTTPServerResponsePart - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - let request = self.unwrapInboundIn(data) - switch request { - case .head(let requestHead): - context.writeAndFlush( - self.wrapOutboundOut(.head(.init(version: .http1_1, status: .ok, headers: requestHead.headers))), - promise: nil - ) - case .body: - break - case .end: - context.writeAndFlush(self.wrapOutboundOut(.end(nil))).assumeIsolated().whenSuccess { - context.close(promise: nil) - } - } - } -} - -final class HTTP200DelayedHandler: ChannelInboundHandler { - typealias InboundIn = HTTPServerRequestPart - typealias OutboundOut = HTTPServerResponsePart - - var pendingBodyParts: Int? - - init(bodyPartsBeforeResponse: Int) { - self.pendingBodyParts = bodyPartsBeforeResponse - } - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - let request = self.unwrapInboundIn(data) - switch request { - case .head: - // Once we have received one response, all further requests are responded to immediately. - if self.pendingBodyParts == nil { - context.writeAndFlush(self.wrapOutboundOut(.head(.init(version: .http1_1, status: .ok))), promise: nil) - context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: nil) - } - case .body: - if let pendingBodyParts = self.pendingBodyParts { - if pendingBodyParts > 0 { - self.pendingBodyParts = pendingBodyParts - 1 - } else { - self.pendingBodyParts = nil - context.writeAndFlush( - self.wrapOutboundOut(.head(.init(version: .http1_1, status: .ok))), - promise: nil - ) - context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: nil) - } - } - case .end: - break - } - } -} - -private let cert = """ - -----BEGIN CERTIFICATE----- - MIICmDCCAYACCQCPC8JDqMh1zzANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJ1 - czAgFw0xODEwMzExNTU1MjJaGA8yMTE4MTAwNzE1NTUyMlowDTELMAkGA1UEBhMC - dXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDiC+TGmbSP/nWWN1tj - yNfnWCU5ATjtIOfdtP6ycx8JSeqkvyNXG21kNUn14jTTU8BglGL2hfVpCbMisUdb - d3LpP8unSsvlOWwORFOViSy4YljSNM/FNoMtavuITA/sEELYgjWkz2o/uHPZHud9 - +JQwGJgqIlMa3mr2IaaUZlWN3D1u88bzJYhpt3YyxRy9+OEoOKy36KdWwhKzV3S8 - kXb0Y1GbAo68jJ9RfzeLy290mIs9qG2y1CNXWO6sxf6B//LaalizZiCfzYAVKcNR - 9oNYsEJc5KB/+DsAGTzR7mL+oiU4h/vwVb2GTDat5C+PFGi6j1ujxYTRPO538ljg - dslnAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFYhA7sw8odOsRO8/DUklBOjPnmn - a078oSumgPXXw6AgcoAJv/Qthjo6CCEtrjYfcA9jaBw9/Tii7mDmqDRS5c9ZPL8+ - NEPdHjFCFBOEvlL6uHOgw0Z9Wz+5yCXnJ8oNUEgc3H2NbbzJF6sMBXSPtFS2NOK8 - OsAI9OodMrDd6+lwljrmFoCCkJHDEfE637IcsbgFKkzhO/oNCRK6OrudG4teDahz - Au4LoEYwT730QKC/VQxxEVZobjn9/sTrq9CZlbPYHxX4fz6e00sX7H9i49vk9zQ5 - 5qCm9ljhrQPSa42Q62PPE2BEEGSP2KBm0J+H3vlvCD6+SNc/nMZjrRmgjrI= - -----END CERTIFICATE----- - """ - -private let key = """ - -----BEGIN PRIVATE KEY----- - MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDiC+TGmbSP/nWW - N1tjyNfnWCU5ATjtIOfdtP6ycx8JSeqkvyNXG21kNUn14jTTU8BglGL2hfVpCbMi - sUdbd3LpP8unSsvlOWwORFOViSy4YljSNM/FNoMtavuITA/sEELYgjWkz2o/uHPZ - Hud9+JQwGJgqIlMa3mr2IaaUZlWN3D1u88bzJYhpt3YyxRy9+OEoOKy36KdWwhKz - V3S8kXb0Y1GbAo68jJ9RfzeLy290mIs9qG2y1CNXWO6sxf6B//LaalizZiCfzYAV - KcNR9oNYsEJc5KB/+DsAGTzR7mL+oiU4h/vwVb2GTDat5C+PFGi6j1ujxYTRPO53 - 8ljgdslnAgMBAAECggEBANZNWFNAnYJ2R5xmVuo/GxFk68Ujd4i4TZpPYbhkk+QG - g8I0w5htlEQQkVHfZx2CpTvq8feuAH/YhlA5qeD5WaPwq26q5qsmyV6tQGDgb9lO - w85l6ySZDbwdVOJe2il/MSB6MclSKvTGNm59chJnfHYsmvY3HHq4qsc2F+tRKYMW - pY75LgEbaTUV69J3cbC1wAeVjv0q/krND+YkhYpTxNZhbazK/FHOCvY+zFu9fg0L - zpwbn5fb6wIvqG7tXp7koa3QMn64AXmO/fb5mBd8G2vBGYnxwb7Egwdg/3Dw+BXu - ynQLP7ixWsE2KNfR9Ce1i3YvEo6QDTv2340I3dntxkECgYEA9vdaL4PGyvEbpim4 - kqz1vuug8Iq0nTVDo6jmgH1o+XdcIbW3imXtgi5zUJpj4oDD7/4aufiJZjG64i/v - phe11xeUvh5QNNOzeMymVDoJut97F97KKKTv7bG8Rpon/WzH2I0SoAkECCwmdWAJ - H3nvOCnXEkpbCqmIUvHVURPRDn8CgYEA6lCk3EzFQlbXs3Sj5op61R3Mscx7/35A - eGv5axzbENHt1so+s3Zvyyi1bo4VBcwnKVCvQjmTuLiqrc9VfX8XdbiTUNnEr2u3 - 992Ja6DEJTZ9gy5WiviwYnwU2HpjwOVNBb17T0NLoRHkDZ6iXj7NZgwizOki5p3j - /hS0pObSIRkCgYEAiEdOGNIarHoHy9VR6H5QzR2xHYssx2NRA8p8B4MsnhxjVqaz - tUcxnJiNQXkwjRiJBrGthdnD2ASxH4dcMsb6rMpyZcbMc5ouewZS8j9khx4zCqUB - 4RPC4eMmBb+jOZEBZlnSYUUYWHokbrij0B61BsTvzUQCoQuUElEoaSkKP3kCgYEA - mwdqXHvK076jjo9w1drvtEu4IDc8H2oH++TsrEr2QiWzaDZ9z71f8BnqGNCW5jQS - AQrqOjXgIArGmqMgXB0Xh4LsrUS4Fpx9ptiD0JsYy8pGtuGUzvQFt9OC80ve7kSI - dnDMwj+zLUmqCrzXjuWcfpUu/UaPGeiDbZuDfcteYhkCgYBLyL5JY7Qd4gVQIhFX - 7Sv3sNJN3KZCQHEzut7IwojaxgpuxiFvgsoXXuYolVCQp32oWbYcE2Yke+hOKsTE - sCMAWZiSGN2Nrfea730IYAXkUm8bpEd3VxDXEEv13nxVeQof+JGMdlkldFGaBRDU - oYQsPj00S3/GA9WDapwe81Wl2A== - -----END PRIVATE KEY----- - """ - -final class BasicInboundTrafficShapingHandler: ChannelDuplexHandler { - typealias OutboundIn = ByteBuffer - typealias InboundIn = ByteBuffer - typealias OutboundOut = ByteBuffer - - enum ReadState { - case flowingFreely - case pausing - case paused - - mutating func pause() { - switch self { - case .flowingFreely: - self = .pausing - case .pausing, .paused: - () // nothing to do - } - } - - mutating func unpause() -> Bool { - switch self { - case .flowingFreely: - return false // no extra `read` needed - case .pausing: - self = .flowingFreely - return false // no extra `read` needed - case .paused: - self = .flowingFreely - return true // yes, we need an extra read - } - } - - mutating func shouldRead() -> Bool { - switch self { - case .flowingFreely: - return true - case .pausing: - self = .paused - return false - case .paused: - return false - } - } - } - - private let targetBytesPerSecond: Int - private var currentSecondBytesSeen: Int = 0 - private var readState: ReadState = .flowingFreely - - init(targetBytesPerSecond: Int) { - self.targetBytesPerSecond = targetBytesPerSecond - } - - func evaluatePause(context: ChannelHandlerContext) { - if self.currentSecondBytesSeen >= self.targetBytesPerSecond { - self.readState.pause() - } else if self.currentSecondBytesSeen < self.targetBytesPerSecond { - if self.readState.unpause() { - context.read() - } - } - } - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - let loopBoundContext = NIOLoopBound(context, eventLoop: context.eventLoop) - defer { - context.fireChannelRead(data) - } - let buffer = Self.unwrapInboundIn(data) - let byteCount = buffer.readableBytes - self.currentSecondBytesSeen += byteCount - context.eventLoop.assumeIsolated().scheduleTask(in: .seconds(1)) { - self.currentSecondBytesSeen -= byteCount - self.evaluatePause(context: loopBoundContext.value) - } - self.evaluatePause(context: context) - } - - func read(context: ChannelHandlerContext) { - if self.readState.shouldRead() { - context.read() - } - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTPClientTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientTests.swift deleted file mode 100644 index 50c3ecb9d..000000000 --- a/Tests/AsyncHTTPClientTests/HTTPClientTests.swift +++ /dev/null @@ -1,4648 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2018-2019 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import AsyncHTTPClient // NOT @testable - tests that need @testable go into HTTPClientInternalTests.swift -import Atomics -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOEmbedded -import NIOFoundationCompat -import NIOHTTP1 -import NIOHTTP2 -import NIOHTTPCompression -import NIOPosix -import NIOSSL -import NIOTestUtils -import NIOTransportServices -import XCTest - -#if canImport(Network) -import Network -#endif - -final class HTTPClientTests: XCTestCaseHTTPClientTestsBaseClass { - func testRequestURI() throws { - let request1 = try Request(url: "https://someserver.com:8888/some/path?foo=bar") - XCTAssertEqual(request1.url.host, "someserver.com") - XCTAssertEqual(request1.url.path, "/some/path") - XCTAssertEqual(request1.url.query!, "foo=bar") - XCTAssertEqual(request1.port, 8888) - XCTAssertTrue(request1.useTLS) - - let request2 = try Request(url: "https://someserver.com/") - XCTAssertEqual(request2.url.path, "") - - let request3 = try Request(url: "unix:///tmp/file") - XCTAssertEqual(request3.host, "") - #if os(Linux) && compiler(>=6.0) && compiler(<6.1) - XCTAssertEqual(request3.url.host, "") - #else - XCTAssertNil(request3.url.host) - #endif - XCTAssertEqual(request3.url.path, "/tmp/file") - XCTAssertEqual(request3.port, 80) - XCTAssertFalse(request3.useTLS) - - let request4 = try Request(url: "http+unix://%2Ftmp%2Ffile/file/path") - XCTAssertEqual(request4.host, "") - XCTAssertEqual(request4.url.host, "/tmp/file") - XCTAssertEqual(request4.url.path, "/file/path") - XCTAssertFalse(request4.useTLS) - - let request5 = try Request(url: "https+unix://%2Ftmp%2Ffile/file/path") - XCTAssertEqual(request5.host, "") - XCTAssertEqual(request5.url.host, "/tmp/file") - XCTAssertEqual(request5.url.path, "/file/path") - XCTAssertTrue(request5.useTLS) - } - - func testBadRequestURI() throws { - XCTAssertThrowsError(try Request(url: "some/path"), "should throw") { error in - XCTAssertEqual(error as! HTTPClientError, HTTPClientError.emptyScheme) - } - XCTAssertThrowsError(try Request(url: "app://somewhere/some/path?foo=bar"), "should throw") { error in - XCTAssertEqual(error as! HTTPClientError, HTTPClientError.unsupportedScheme("app")) - } - XCTAssertThrowsError(try Request(url: "https:/foo"), "should throw") { error in - XCTAssertEqual(error as! HTTPClientError, HTTPClientError.emptyHost) - } - XCTAssertThrowsError(try Request(url: "http+unix:///path"), "should throw") { error in - XCTAssertEqual(error as! HTTPClientError, HTTPClientError.missingSocketPath) - } - } - - func testSchemaCasing() throws { - XCTAssertNoThrow(try Request(url: "hTTpS://someserver.com:8888/some/path?foo=bar")) - XCTAssertNoThrow(try Request(url: "uNIx:///some/path")) - XCTAssertNoThrow(try Request(url: "hTtP+uNIx://%2Fsome%2Fpath/")) - XCTAssertNoThrow(try Request(url: "hTtPS+uNIx://%2Fsome%2Fpath/")) - } - - func testURLSocketPathInitializers() throws { - let url1 = URL(httpURLWithSocketPath: "/tmp/file") - XCTAssertNotNil(url1) - if let url = url1 { - XCTAssertEqual(url.scheme, "http+unix") - XCTAssertEqual(url.host, "/tmp/file") - XCTAssertEqual(url.path, "/") - XCTAssertEqual(url.absoluteString, "http+unix://%2Ftmp%2Ffile/") - } - - let url2 = URL(httpURLWithSocketPath: "/tmp/file", uri: "/file/path") - XCTAssertNotNil(url2) - if let url = url2 { - XCTAssertEqual(url.scheme, "http+unix") - XCTAssertEqual(url.host, "/tmp/file") - XCTAssertEqual(url.path, "/file/path") - XCTAssertEqual(url.absoluteString, "http+unix://%2Ftmp%2Ffile/file/path") - } - - let url3 = URL(httpURLWithSocketPath: "/tmp/file", uri: "file/path") - XCTAssertNotNil(url3) - if let url = url3 { - XCTAssertEqual(url.scheme, "http+unix") - XCTAssertEqual(url.host, "/tmp/file") - XCTAssertEqual(url.path, "/file/path") - XCTAssertEqual(url.absoluteString, "http+unix://%2Ftmp%2Ffile/file/path") - } - - let url4 = URL(httpURLWithSocketPath: "/tmp/file with spacesと漢字", uri: "file/path") - XCTAssertNotNil(url4) - if let url = url4 { - XCTAssertEqual(url.scheme, "http+unix") - XCTAssertEqual(url.host, "/tmp/file with spacesと漢字") - XCTAssertEqual(url.path, "/file/path") - XCTAssertEqual( - url.absoluteString, - "http+unix://%2Ftmp%2Ffile%20with%20spaces%E3%81%A8%E6%BC%A2%E5%AD%97/file/path" - ) - } - - let url5 = URL(httpsURLWithSocketPath: "/tmp/file") - XCTAssertNotNil(url5) - if let url = url5 { - XCTAssertEqual(url.scheme, "https+unix") - XCTAssertEqual(url.host, "/tmp/file") - XCTAssertEqual(url.path, "/") - XCTAssertEqual(url.absoluteString, "https+unix://%2Ftmp%2Ffile/") - } - - let url6 = URL(httpsURLWithSocketPath: "/tmp/file", uri: "/file/path") - XCTAssertNotNil(url6) - if let url = url6 { - XCTAssertEqual(url.scheme, "https+unix") - XCTAssertEqual(url.host, "/tmp/file") - XCTAssertEqual(url.path, "/file/path") - XCTAssertEqual(url.absoluteString, "https+unix://%2Ftmp%2Ffile/file/path") - } - - let url7 = URL(httpsURLWithSocketPath: "/tmp/file", uri: "file/path") - XCTAssertNotNil(url7) - if let url = url7 { - XCTAssertEqual(url.scheme, "https+unix") - XCTAssertEqual(url.host, "/tmp/file") - XCTAssertEqual(url.path, "/file/path") - XCTAssertEqual(url.absoluteString, "https+unix://%2Ftmp%2Ffile/file/path") - } - - let url8 = URL(httpsURLWithSocketPath: "/tmp/file with spacesと漢字", uri: "file/path") - XCTAssertNotNil(url8) - if let url = url8 { - XCTAssertEqual(url.scheme, "https+unix") - XCTAssertEqual(url.host, "/tmp/file with spacesと漢字") - XCTAssertEqual(url.path, "/file/path") - XCTAssertEqual( - url.absoluteString, - "https+unix://%2Ftmp%2Ffile%20with%20spaces%E3%81%A8%E6%BC%A2%E5%AD%97/file/path" - ) - } - } - - func testBadUnixWithBaseURL() { - let badUnixBaseURL = URL(string: "/foo", relativeTo: URL(string: "unix:")!)! - XCTAssertEqual(badUnixBaseURL.baseURL?.path, "") - XCTAssertThrowsError(try Request(url: badUnixBaseURL)) { error in - XCTAssertEqual(error as! HTTPClientError, HTTPClientError.missingSocketPath) - } - } - - func testConvenienceExecuteMethods() throws { - XCTAssertEqual( - ["GET"[...]], - try self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "echo-method").wait().headers[ - canonicalForm: "X-Method-Used" - ] - ) - XCTAssertEqual( - ["POST"[...]], - try self.defaultClient.post(url: self.defaultHTTPBinURLPrefix + "echo-method").wait().headers[ - canonicalForm: "X-Method-Used" - ] - ) - XCTAssertEqual( - ["PATCH"[...]], - try self.defaultClient.patch(url: self.defaultHTTPBinURLPrefix + "echo-method").wait().headers[ - canonicalForm: "X-Method-Used" - ] - ) - XCTAssertEqual( - ["PUT"[...]], - try self.defaultClient.put(url: self.defaultHTTPBinURLPrefix + "echo-method").wait().headers[ - canonicalForm: "X-Method-Used" - ] - ) - XCTAssertEqual( - ["DELETE"[...]], - try self.defaultClient.delete(url: self.defaultHTTPBinURLPrefix + "echo-method").wait().headers[ - canonicalForm: "X-Method-Used" - ] - ) - XCTAssertEqual( - ["GET"[...]], - try self.defaultClient.execute(url: self.defaultHTTPBinURLPrefix + "echo-method").wait().headers[ - canonicalForm: "X-Method-Used" - ] - ) - XCTAssertEqual( - ["CHECKOUT"[...]], - try self.defaultClient.execute(.CHECKOUT, url: self.defaultHTTPBinURLPrefix + "echo-method").wait().headers[ - canonicalForm: "X-Method-Used" - ] - ) - } - - func testConvenienceExecuteMethodsOverSocket() throws { - XCTAssertNoThrow( - try TemporaryFileHelpers.withTemporaryUnixDomainSocketPathName { path in - let localSocketPathHTTPBin = HTTPBin(bindTarget: .unixDomainSocket(path)) - defer { - XCTAssertNoThrow(try localSocketPathHTTPBin.shutdown()) - } - - XCTAssertEqual( - ["GET"[...]], - try self.defaultClient.execute(socketPath: path, urlPath: "echo-method").wait().headers[ - canonicalForm: "X-Method-Used" - ] - ) - XCTAssertEqual( - ["GET"[...]], - try self.defaultClient.execute(.GET, socketPath: path, urlPath: "echo-method").wait().headers[ - canonicalForm: "X-Method-Used" - ] - ) - XCTAssertEqual( - ["POST"[...]], - try self.defaultClient.execute(.POST, socketPath: path, urlPath: "echo-method").wait().headers[ - canonicalForm: "X-Method-Used" - ] - ) - } - ) - } - - func testConvenienceExecuteMethodsOverSecureSocket() throws { - XCTAssertNoThrow( - try TemporaryFileHelpers.withTemporaryUnixDomainSocketPathName { path in - let localSocketPathHTTPBin = HTTPBin( - .http1_1(ssl: true, compress: false), - bindTarget: .unixDomainSocket(path) - ) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: HTTPClient.Configuration(certificateVerification: .none) - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localSocketPathHTTPBin.shutdown()) - } - - XCTAssertEqual( - ["GET"[...]], - try localClient.execute(secureSocketPath: path, urlPath: "echo-method").wait().headers[ - canonicalForm: "X-Method-Used" - ] - ) - XCTAssertEqual( - ["GET"[...]], - try localClient.execute(.GET, secureSocketPath: path, urlPath: "echo-method").wait().headers[ - canonicalForm: "X-Method-Used" - ] - ) - XCTAssertEqual( - ["POST"[...]], - try localClient.execute(.POST, secureSocketPath: path, urlPath: "echo-method").wait().headers[ - canonicalForm: "X-Method-Used" - ] - ) - } - ) - } - - func testGet() throws { - let response = try self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "get").wait() - XCTAssertEqual(.ok, response.status) - } - - func testGetWithDifferentEventLoopBackpressure() throws { - let request = try HTTPClient.Request(url: self.defaultHTTPBinURLPrefix + "events/10/1") - let delegate = TestHTTPDelegate(backpressureEventLoop: self.serverGroup.next()) - let task = self.defaultClient.execute(request: request, delegate: delegate) - try task.wait() - } - - func testPost() throws { - let response = try self.defaultClient.post(url: self.defaultHTTPBinURLPrefix + "post", body: .string("1234")) - .wait() - let bytes = response.body.flatMap { $0.getData(at: 0, length: $0.readableBytes) } - let data = try JSONDecoder().decode(RequestInfo.self, from: bytes!) - - XCTAssertEqual(.ok, response.status) - XCTAssertEqual("1234", data.data) - } - - func testPostWithGenericBody() throws { - let bodyData = Array(Array("hello, world!").lazy.map { $0.uppercased().first!.asciiValue! }) - - let response = try self.defaultClient.post(url: self.defaultHTTPBinURLPrefix + "post", body: .bytes(bodyData)) - .wait() - let bytes = response.body.flatMap { $0.getData(at: 0, length: $0.readableBytes) } - let data = try JSONDecoder().decode(RequestInfo.self, from: bytes!) - - XCTAssertEqual(.ok, response.status) - XCTAssertEqual("HELLO, WORLD!", data.data) - } - - func testPostWithFoundationDataBody() throws { - let bodyData = Data("hello, world!".utf8) - - let response = try self.defaultClient.post(url: self.defaultHTTPBinURLPrefix + "post", body: .data(bodyData)) - .wait() - let bytes = response.body.flatMap { $0.getData(at: 0, length: $0.readableBytes) } - let data = try JSONDecoder().decode(RequestInfo.self, from: bytes!) - - XCTAssertEqual(.ok, response.status) - XCTAssertEqual("hello, world!", data.data) - } - - func testGetHttps() throws { - let localHTTPBin = HTTPBin(.http1_1(ssl: true)) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: HTTPClient.Configuration(certificateVerification: .none) - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - - let response = try localClient.get(url: "https://localhost/(localHTTPBin.port)/get").wait() - XCTAssertEqual(.ok, response.status) - } - - func testGetHttpsWithIP() throws { - let localHTTPBin = HTTPBin(.http1_1(ssl: true)) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: HTTPClient.Configuration(certificateVerification: .none) - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - - let response = try localClient.get(url: "https://127.0.0.1/(localHTTPBin.port)/get").wait() - XCTAssertEqual(.ok, response.status) - } - - func testGetHTTPSWorksOnMTELGWithIP() throws { - // Same test as above but this one will use NIO on Sockets even on Apple platforms, just to make sure - // this works. - let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { - XCTAssertNoThrow(try group.syncShutdownGracefully()) - } - let localHTTPBin = HTTPBin(.http1_1(ssl: true)) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(group), - configuration: HTTPClient.Configuration(certificateVerification: .none) - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - - let response = try localClient.get(url: "https://127.0.0.1/(localHTTPBin.port)/get").wait() - XCTAssertEqual(.ok, response.status) - } - - func testGetHttpsWithIPv6() throws { - try XCTSkipUnless(canBindIPv6Loopback, "Requires IPv6") - let localHTTPBin = HTTPBin(.http1_1(ssl: true), bindTarget: .localhostIPv6RandomPort) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: HTTPClient.Configuration(certificateVerification: .none) - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - var response: HTTPClient.Response? - XCTAssertNoThrow(response = try localClient.get(url: "https://[::1]/(localHTTPBin.port)/get").wait()) - XCTAssertEqual(.ok, response?.status) - } - - func testGetHTTPSWorksOnMTELGWithIPv6() throws { - try XCTSkipUnless(canBindIPv6Loopback, "Requires IPv6") - // Same test as above but this one will use NIO on Sockets even on Apple platforms, just to make sure - // this works. - let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { - XCTAssertNoThrow(try group.syncShutdownGracefully()) - } - let localHTTPBin = HTTPBin(.http1_1(ssl: true), bindTarget: .localhostIPv6RandomPort) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(group), - configuration: HTTPClient.Configuration(certificateVerification: .none) - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - var response: HTTPClient.Response? - XCTAssertNoThrow(response = try localClient.get(url: "https://[::1]/(localHTTPBin.port)/get").wait()) - XCTAssertEqual(.ok, response?.status) - } - - func testPostHttps() throws { - let localHTTPBin = HTTPBin(.http1_1(ssl: true)) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: HTTPClient.Configuration(certificateVerification: .none) - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - - let request = try Request( - url: "https://localhost/(localHTTPBin.port)/post", - method: .POST, - body: .string("1234") - ) - - let response = try localClient.execute(request: request).wait() - let bytes = response.body.flatMap { $0.getData(at: 0, length: $0.readableBytes) } - let data = try JSONDecoder().decode(RequestInfo.self, from: bytes!) - - XCTAssertEqual(.ok, response.status) - XCTAssertEqual("1234", data.data) - } - - func testHttpRedirect() throws { - let httpsBin = HTTPBin(.http1_1(ssl: true)) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: HTTPClient.Configuration( - certificateVerification: .none, - redirectConfiguration: .follow(max: 10, allowCycles: true) - ) - ) - - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try httpsBin.shutdown()) - } - - var response = try localClient.get(url: self.defaultHTTPBinURLPrefix + "redirect/302").wait() - XCTAssertEqual(response.status, .ok) - XCTAssertEqual(response.url?.absoluteString, self.defaultHTTPBinURLPrefix + "ok") - XCTAssertEqual( - response.history.map(\.request.url.absoluteString), - [ - self.defaultHTTPBinURLPrefix + "redirect/302", - self.defaultHTTPBinURLPrefix + "ok", - ] - ) - - response = try localClient.get(url: self.defaultHTTPBinURLPrefix + "redirect/https?port=\(httpsBin.port)") - .wait() - XCTAssertEqual(response.status, .ok) - - XCTAssertNoThrow( - try TemporaryFileHelpers.withTemporaryUnixDomainSocketPathName { httpSocketPath in - XCTAssertNoThrow( - try TemporaryFileHelpers.withTemporaryUnixDomainSocketPathName { httpsSocketPath in - let socketHTTPBin = HTTPBin(bindTarget: .unixDomainSocket(httpSocketPath)) - let socketHTTPSBin = HTTPBin( - .http1_1(ssl: true), - bindTarget: .unixDomainSocket(httpsSocketPath) - ) - defer { - XCTAssertNoThrow(try socketHTTPBin.shutdown()) - XCTAssertNoThrow(try socketHTTPSBin.shutdown()) - } - - // From HTTP or HTTPS to HTTP+UNIX should fail to redirect - var targetURL = - "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/ok" - var request = try Request( - url: self.defaultHTTPBinURLPrefix + "redirect/target", - method: .GET, - headers: ["X-Target-Redirect-URL": targetURL], - body: nil - ) - - var response = try localClient.execute(request: request).wait() - XCTAssertEqual(response.status, .found) - XCTAssertEqual(response.headers.first(name: "Location"), targetURL) - XCTAssertEqual(response.url, request.url) - XCTAssertEqual(response.history.map(\.request.url), [request.url]) - - request = try Request( - url: "https://localhost/(httpsBin.port)/redirect/target", - method: .GET, - headers: ["X-Target-Redirect-URL": targetURL], - body: nil - ) - - response = try localClient.execute(request: request).wait() - XCTAssertEqual(response.status, .found) - XCTAssertEqual(response.headers.first(name: "Location"), targetURL) - XCTAssertEqual(response.url, request.url) - XCTAssertEqual(response.history.map(\.request.url), [request.url]) - - // From HTTP or HTTPS to HTTPS+UNIX should also fail to redirect - targetURL = - "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/ok" - request = try Request( - url: self.defaultHTTPBinURLPrefix + "redirect/target", - method: .GET, - headers: ["X-Target-Redirect-URL": targetURL], - body: nil - ) - - response = try localClient.execute(request: request).wait() - XCTAssertEqual(response.status, .found) - XCTAssertEqual(response.headers.first(name: "Location"), targetURL) - XCTAssertEqual(response.url, request.url) - XCTAssertEqual(response.history.map(\.request.url), [request.url]) - - request = try Request( - url: "https://localhost/(httpsBin.port)/redirect/target", - method: .GET, - headers: ["X-Target-Redirect-URL": targetURL], - body: nil - ) - - response = try localClient.execute(request: request).wait() - XCTAssertEqual(response.status, .found) - XCTAssertEqual(response.headers.first(name: "Location"), targetURL) - XCTAssertEqual(response.url, request.url) - XCTAssertEqual(response.history.map(\.request.url), [request.url]) - - // ... while HTTP+UNIX to HTTP, HTTPS, or HTTP(S)+UNIX should succeed - targetURL = self.defaultHTTPBinURLPrefix + "ok" - request = try Request( - url: - "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", - method: .GET, - headers: ["X-Target-Redirect-URL": targetURL], - body: nil - ) - - response = try localClient.execute(request: request).wait() - XCTAssertEqual(response.status, .ok) - XCTAssertEqual(response.url?.absoluteString, targetURL) - XCTAssertEqual( - response.history.map(\.request.url.absoluteString), - [request.url.absoluteString, targetURL] - ) - - targetURL = "https://localhost/(httpsBin.port)/ok" - request = try Request( - url: - "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", - method: .GET, - headers: ["X-Target-Redirect-URL": targetURL], - body: nil - ) - - response = try localClient.execute(request: request).wait() - XCTAssertEqual(response.status, .ok) - XCTAssertEqual(response.url?.absoluteString, targetURL) - XCTAssertEqual( - response.history.map(\.request.url.absoluteString), - [request.url.absoluteString, targetURL] - ) - - targetURL = - "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/ok" - request = try Request( - url: - "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", - method: .GET, - headers: ["X-Target-Redirect-URL": targetURL], - body: nil - ) - - response = try localClient.execute(request: request).wait() - XCTAssertEqual(response.status, .ok) - XCTAssertEqual(response.url?.absoluteString, targetURL) - XCTAssertEqual( - response.history.map(\.request.url.absoluteString), - [request.url.absoluteString, targetURL] - ) - - targetURL = - "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/ok" - request = try Request( - url: - "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", - method: .GET, - headers: ["X-Target-Redirect-URL": targetURL], - body: nil - ) - - response = try localClient.execute(request: request).wait() - XCTAssertEqual(response.status, .ok) - XCTAssertEqual(response.url?.absoluteString, targetURL) - XCTAssertEqual( - response.history.map(\.request.url.absoluteString), - [request.url.absoluteString, targetURL] - ) - - // ... and HTTPS+UNIX to HTTP, HTTPS, or HTTP(S)+UNIX should succeed - targetURL = self.defaultHTTPBinURLPrefix + "ok" - request = try Request( - url: - "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", - method: .GET, - headers: ["X-Target-Redirect-URL": targetURL], - body: nil - ) - - response = try localClient.execute(request: request).wait() - XCTAssertEqual(response.status, .ok) - XCTAssertEqual(response.url?.absoluteString, targetURL) - XCTAssertEqual( - response.history.map(\.request.url.absoluteString), - [request.url.absoluteString, targetURL] - ) - - targetURL = "https://localhost/(httpsBin.port)/ok" - request = try Request( - url: - "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", - method: .GET, - headers: ["X-Target-Redirect-URL": targetURL], - body: nil - ) - - response = try localClient.execute(request: request).wait() - XCTAssertEqual(response.status, .ok) - XCTAssertEqual(response.url?.absoluteString, targetURL) - XCTAssertEqual( - response.history.map(\.request.url.absoluteString), - [request.url.absoluteString, targetURL] - ) - - targetURL = - "http+unix://\(httpSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/ok" - request = try Request( - url: - "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", - method: .GET, - headers: ["X-Target-Redirect-URL": targetURL], - body: nil - ) - - response = try localClient.execute(request: request).wait() - XCTAssertEqual(response.status, .ok) - XCTAssertEqual(response.url?.absoluteString, targetURL) - XCTAssertEqual( - response.history.map(\.request.url.absoluteString), - [request.url.absoluteString, targetURL] - ) - - targetURL = - "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/ok" - request = try Request( - url: - "https+unix://\(httpsSocketPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)/redirect/target", - method: .GET, - headers: ["X-Target-Redirect-URL": targetURL], - body: nil - ) - - response = try localClient.execute(request: request).wait() - XCTAssertEqual(response.status, .ok) - XCTAssertEqual(response.url?.absoluteString, targetURL) - XCTAssertEqual( - response.history.map(\.request.url.absoluteString), - [request.url.absoluteString, targetURL] - ) - } - ) - } - ) - } - - func testHttpHostRedirect() { - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: HTTPClient.Configuration( - certificateVerification: .none, - redirectConfiguration: .follow(max: 10, allowCycles: true) - ) - ) - - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - } - - let url = self.defaultHTTPBinURLPrefix + "redirect/loopback?port=\(self.defaultHTTPBin.port)" - var maybeResponse: HTTPClient.Response? - XCTAssertNoThrow(maybeResponse = try localClient.get(url: url).wait()) - guard let response = maybeResponse, let body = response.body else { - XCTFail("request failed") - return - } - let hostName = try? JSONDecoder().decode(RequestInfo.self, from: body).data - XCTAssertEqual("127.0.0.1:\(self.defaultHTTPBin.port)", hostName) - } - - func testPercentEncoded() throws { - let response = try self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "percent%20encoded").wait() - XCTAssertEqual(.ok, response.status) - } - - func testPercentEncodedBackslash() throws { - let response = try self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "percent%2Fencoded/hello").wait() - XCTAssertEqual(.ok, response.status) - } - - func testLeadingSlashRelativeURL() throws { - let noLeadingSlashURL = URL( - string: "percent%2Fencoded/hello", - relativeTo: URL(string: self.defaultHTTPBinURLPrefix)! - )! - let withLeadingSlashURL = URL( - string: "/percent%2Fencoded/hello", - relativeTo: URL(string: self.defaultHTTPBinURLPrefix)! - )! - - let noLeadingSlashURLRequest = try HTTPClient.Request(url: noLeadingSlashURL, method: .GET) - let withLeadingSlashURLRequest = try HTTPClient.Request(url: withLeadingSlashURL, method: .GET) - - let noLeadingSlashURLResponse = try self.defaultClient.execute(request: noLeadingSlashURLRequest).wait() - let withLeadingSlashURLResponse = try self.defaultClient.execute(request: withLeadingSlashURLRequest).wait() - - XCTAssertEqual(noLeadingSlashURLResponse.status, .ok) - XCTAssertEqual(withLeadingSlashURLResponse.status, .ok) - } - - func testMultipleContentLengthHeaders() throws { - let body = ByteBuffer(string: "hello world!") - - var headers = HTTPHeaders() - headers.add(name: "Content-Length", value: "12") - let request = try Request( - url: self.defaultHTTPBinURLPrefix + "post", - method: .POST, - headers: headers, - body: .byteBuffer(body) - ) - let response = try self.defaultClient.execute(request: request).wait() - // if the library adds another content length header we'll get a bad request error. - XCTAssertEqual(.ok, response.status) - } - - func testStreaming() throws { - var request = try Request(url: self.defaultHTTPBinURLPrefix + "events/10/1") - request.headers.add(name: "Accept", value: "text/event-stream") - - let delegate = CountingDelegate() - let count = try self.defaultClient.execute(request: request, delegate: delegate).wait() - - XCTAssertEqual(10, count) - } - - func testFileDownload() throws { - var request = try Request(url: self.defaultHTTPBinURLPrefix + "events/10/content-length") - request.headers.add(name: "Accept", value: "text/event-stream") - - let response = - try TemporaryFileHelpers.withTemporaryFilePath { path -> FileDownloadDelegate.Response in - let delegate = try FileDownloadDelegate(path: path) - - let response = try self.defaultClient.execute( - request: request, - delegate: delegate - ) - .wait() - - try XCTAssertEqual(50, TemporaryFileHelpers.fileSize(path: path)) - - return response - } - - XCTAssertEqual(.ok, response.head.status) - XCTAssertEqual("50", response.head.headers.first(name: "content-length")) - - XCTAssertEqual(50, response.totalBytes) - XCTAssertEqual(50, response.receivedBytes) - } - - func testFileDownloadError() throws { - var request = try Request(url: self.defaultHTTPBinURLPrefix + "not-found") - request.headers.add(name: "Accept", value: "text/event-stream") - - let response = - try TemporaryFileHelpers.withTemporaryFilePath { path -> FileDownloadDelegate.Response in - let delegate = try FileDownloadDelegate( - path: path, - reportHead: { - XCTAssertEqual($0.status, .notFound) - } - ) - - let response = try self.defaultClient.execute( - request: request, - delegate: delegate - ) - .wait() - - XCTAssertFalse(TemporaryFileHelpers.fileExists(path: path)) - - return response - } - - XCTAssertEqual(.notFound, response.head.status) - XCTAssertFalse(response.head.headers.contains(name: "content-length")) - - XCTAssertEqual(nil, response.totalBytes) - XCTAssertEqual(0, response.receivedBytes) - } - - func testFileDownloadCustomError() throws { - let request = try Request(url: self.defaultHTTPBinURLPrefix + "get") - struct CustomError: Equatable, Error {} - - try TemporaryFileHelpers.withTemporaryFilePath { path in - let delegate = try FileDownloadDelegate( - path: path, - reportHead: { task, head in - XCTAssertEqual(head.status, .ok) - task.fail(reason: CustomError()) - }, - reportProgress: { _, _ in - XCTFail("should never be called") - } - ) - XCTAssertThrowsError( - try self.defaultClient.execute( - request: request, - delegate: delegate - ) - .wait() - ) { error in - XCTAssertEqualTypeAndValue(error, CustomError()) - } - - XCTAssertFalse(TemporaryFileHelpers.fileExists(path: path)) - } - } - - func testRemoteClose() { - XCTAssertThrowsError(try self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "close").wait()) { - XCTAssertEqual($0 as? HTTPClientError, .remoteConnectionClosed) - } - } - - func testReadTimeout() { - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: HTTPClient.Configuration(timeout: HTTPClient.Configuration.Timeout(read: .milliseconds(150))) - ) - - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - } - - XCTAssertThrowsError(try localClient.get(url: self.defaultHTTPBinURLPrefix + "wait").wait()) { - XCTAssertEqual($0 as? HTTPClientError, .readTimeout) - } - } - - func testWriteTimeout() throws { - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: HTTPClient.Configuration(timeout: HTTPClient.Configuration.Timeout(write: .nanoseconds(10))) - ) - - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - } - - // Create a request that writes a chunk, then waits longer than the configured write timeout, - // and then writes again. This should trigger a write timeout error. - let request = try HTTPClient.Request( - url: self.defaultHTTPBinURLPrefix + "post", - method: .POST, - headers: ["transfer-encoding": "chunked"], - body: .stream { streamWriter in - _ = streamWriter.write(.byteBuffer(.init())) - - let promise = localClient.eventLoopGroup.next().makePromise(of: Void.self) - localClient.eventLoopGroup.next().scheduleTask(in: .milliseconds(3)) { - streamWriter.write(.byteBuffer(.init())).cascade(to: promise) - } - - return promise.futureResult - } - ) - - XCTAssertThrowsError(try localClient.execute(request: request).wait()) { - XCTAssertEqual($0 as? HTTPClientError, .writeTimeout) - } - } - - func testConnectTimeout() throws { - #if os(Linux) - // 198.51.100.254 is reserved for documentation only and therefore should not accept any TCP connection - let url = "http://198.51.100.254/get" - #else - // on macOS we can use the TCP backlog behaviour when the queue is full to simulate a non reachable server. - // this makes this test a bit more stable if `198.51.100.254` actually responds to connection attempt. - // The backlog behaviour on Linux can not be used to simulate a non-reachable server. - // Linux sends a `SYN/ACK` back even if the `backlog` queue is full as it has two queues. - // The second queue is not limit by `ChannelOptions.backlog` but by `/proc/sys/net/ipv4/tcp_max_syn_backlog`. - - let serverChannel = try ServerBootstrap(group: self.serverGroup) - .serverChannelOption(ChannelOptions.backlog, value: 1) - .serverChannelOption(ChannelOptions.autoRead, value: false) - .bind(host: "127.0.0.1", port: 0) - .wait() - defer { - XCTAssertNoThrow(try serverChannel.close().wait()) - } - let port = serverChannel.localAddress!.port! - let firstClientChannel = try ClientBootstrap(group: self.serverGroup) - .connect(host: "127.0.0.1", port: port) - .wait() - defer { - XCTAssertNoThrow(try firstClientChannel.close().wait()) - } - let url = "http://localhost/(port)/get" - #endif - - let httpClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: .init(timeout: .init(connect: .milliseconds(100), read: .milliseconds(150))) - ) - - defer { - XCTAssertNoThrow(try httpClient.syncShutdown()) - } - - XCTAssertThrowsError(try httpClient.get(url: url).wait()) { - XCTAssertEqualTypeAndValue($0, HTTPClientError.connectTimeout) - } - } - - func testDeadline() { - XCTAssertThrowsError( - try self.defaultClient.get( - url: self.defaultHTTPBinURLPrefix + "wait", - deadline: .now() + .milliseconds(150) - ).wait() - ) { - XCTAssertEqual($0 as? HTTPClientError, .deadlineExceeded) - } - } - - func testCancel() throws { - let queue = DispatchQueue(label: "nio-test") - let request = try Request(url: self.defaultHTTPBinURLPrefix + "wait") - let task = self.defaultClient.execute(request: request, delegate: TestHTTPDelegate()) - - queue.asyncAfter(deadline: .now() + .milliseconds(100)) { - task.cancel() - } - - XCTAssertThrowsError(try task.wait(), "Should fail") { error in - guard case let error = error as? HTTPClientError, error == .cancelled else { - return XCTFail("Should fail with cancelled") - } - } - } - - func testStressCancel() throws { - let request = try Request(url: self.defaultHTTPBinURLPrefix + "wait", method: .GET) - let tasks = (1...100).map { _ -> HTTPClient.Task in - let task = self.defaultClient.execute(request: request, delegate: TestHTTPDelegate()) - task.cancel() - return task - } - - for task in tasks { - switch (Result { try task.futureResult.timeout(after: .seconds(10)).wait() }) { - case .success: - XCTFail("Shouldn't succeed") - return - case .failure(let error): - guard let clientError = error as? HTTPClientError, clientError == .cancelled else { - XCTFail("Unexpected error: \(error)") - return - } - } - } - } - - func testHTTPClientAuthorization() { - var authorization = HTTPClient.Authorization.basic(username: "aladdin", password: "opensesame") - XCTAssertEqual(authorization.headerValue, "Basic YWxhZGRpbjpvcGVuc2VzYW1l") - - authorization = HTTPClient.Authorization.bearer(tokens: "mF_9.B5f-4.1JqM") - XCTAssertEqual(authorization.headerValue, "Bearer mF_9.B5f-4.1JqM") - } - - func testProxyPlaintext() throws { - let localHTTPBin = HTTPBin(proxy: .simulate(authorization: nil)) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: .init(proxy: .server(host: "localhost", port: localHTTPBin.port)) - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - let res = try localClient.get(url: "http://test/ok").wait() - XCTAssertEqual(res.status, .ok) - } - - func testProxyTLS() throws { - let localHTTPBin = HTTPBin(.http1_1(ssl: true), proxy: .simulate(authorization: nil)) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: .init( - certificateVerification: .none, - proxy: .server(host: "localhost", port: localHTTPBin.port) - ) - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - let res = try localClient.get(url: "https://test/ok").wait() - XCTAssertEqual(res.status, .ok) - } - - func testProxyPlaintextWithCorrectlyAuthorization() throws { - let localHTTPBin = HTTPBin(proxy: .simulate(authorization: "Basic YWxhZGRpbjpvcGVuc2VzYW1l")) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: .init( - proxy: .server( - host: "localhost", - port: localHTTPBin.port, - authorization: .basic(username: "aladdin", password: "opensesame") - ) - ) - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - let res = try localClient.get(url: "http://test/ok").wait() - XCTAssertEqual(res.status, .ok) - } - - func testProxyPlaintextWithIncorrectlyAuthorization() throws { - let localHTTPBin = HTTPBin(proxy: .simulate(authorization: "Basic YWxhZGRpbjpvcGVuc2VzYW1l")) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: .init( - proxy: .server( - host: "localhost", - port: localHTTPBin.port, - authorization: .basic( - username: "aladdin", - password: "opensesamefoo" - ) - ) - ).enableFastFailureModeForTesting() - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - XCTAssertThrowsError(try localClient.get(url: "http://test/ok").wait(), "Should fail") { error in - guard case let error = error as? HTTPClientError, error == .proxyAuthenticationRequired else { - return XCTFail("Should fail with HTTPClientError.proxyAuthenticationRequired") - } - } - } - - func testUploadStreaming() throws { - let body: HTTPClient.Body = .stream(contentLength: 8) { writer in - let buffer = ByteBuffer(string: "1234") - return writer.write(.byteBuffer(buffer)).flatMap { - let buffer = ByteBuffer(string: "4321") - return writer.write(.byteBuffer(buffer)) - } - } - - let response = try self.defaultClient.post(url: self.defaultHTTPBinURLPrefix + "post", body: body).wait() - let bytes = response.body.flatMap { $0.getData(at: 0, length: $0.readableBytes) } - let data = try JSONDecoder().decode(RequestInfo.self, from: bytes!) - - XCTAssertEqual(.ok, response.status) - XCTAssertEqual("12344321", data.data) - } - - func testEventLoopArgument() throws { - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: HTTPClient.Configuration(redirectConfiguration: .follow(max: 10, allowCycles: true)) - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - } - - final class EventLoopValidatingDelegate: HTTPClientResponseDelegate { - typealias Response = Bool - - let eventLoop: EventLoop - let result = NIOLockedValueBox(false) - - init(eventLoop: EventLoop) { - self.eventLoop = eventLoop - } - - func didReceiveHead(task: HTTPClient.Task, _ head: HTTPResponseHead) -> EventLoopFuture { - self.result.withLockedValue { $0 = task.eventLoop === self.eventLoop } - return task.eventLoop.makeSucceededFuture(()) - } - - func didFinishRequest(task: HTTPClient.Task) throws -> Bool { - self.result.withLockedValue { $0 } - } - } - - let eventLoop = self.clientGroup.next() - let delegate = EventLoopValidatingDelegate(eventLoop: eventLoop) - var request = try HTTPClient.Request(url: self.defaultHTTPBinURLPrefix + "get") - var response = try localClient.execute( - request: request, - delegate: delegate, - eventLoop: .delegate(on: eventLoop) - ).wait() - XCTAssertEqual(true, response) - - // redirect - request = try HTTPClient.Request(url: self.defaultHTTPBinURLPrefix + "redirect/302") - response = try localClient.execute(request: request, delegate: delegate, eventLoop: .delegate(on: eventLoop)) - .wait() - XCTAssertEqual(true, response) - } - - func testDecompression() throws { - let localHTTPBin = HTTPBin(.http1_1(compress: true)) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: .init(decompression: .enabled(limit: .none)) - ) - - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - - var body = "" - for _ in 1...1000 { - body += - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." - } - - for algorithm in [nil, "gzip", "deflate"] { - var request = try HTTPClient.Request(url: "http://localhost/(localHTTPBin.port)/post", method: .POST) - request.body = .string(body) - if let algorithm = algorithm { - request.headers.add(name: "Accept-Encoding", value: algorithm) - } - - let response = try localClient.execute(request: request).wait() - let bytes = response.body!.getData(at: 0, length: response.body!.readableBytes)! - let data = try JSONDecoder().decode(RequestInfo.self, from: bytes) - - XCTAssertEqual(.ok, response.status) - XCTAssertGreaterThan(body.count, response.headers["Content-Length"].first.flatMap { Int($0) }!) - if let algorithm = algorithm { - XCTAssertEqual(algorithm, response.headers["Content-Encoding"].first) - } else { - XCTAssertEqual("deflate", response.headers["Content-Encoding"].first) - } - XCTAssertEqual(body, data.data) - } - } - - func testDecompressionHTTP2() throws { - let localHTTPBin = HTTPBin(.http2(compress: true)) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: .init( - certificateVerification: .none, - decompression: .enabled(limit: .none) - ) - ) - - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - - var body = "" - for _ in 1...1000 { - body += - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." - } - - for algorithm: String? in [nil] { - var request = try HTTPClient.Request(url: "https://localhost/(localHTTPBin.port)/post", method: .POST) - request.body = .string(body) - if let algorithm = algorithm { - request.headers.add(name: "Accept-Encoding", value: algorithm) - } - - let response = try localClient.execute(request: request).wait() - var responseBody = try XCTUnwrap(response.body) - let data = try responseBody.readJSONDecodable(RequestInfo.self, length: responseBody.readableBytes) - - XCTAssertEqual(.ok, response.status) - let contentLength = try XCTUnwrap(response.headers["Content-Length"].first.flatMap { Int($0) }) - XCTAssertGreaterThan(body.count, contentLength) - if let algorithm = algorithm { - XCTAssertEqual(algorithm, response.headers["Content-Encoding"].first) - } else { - XCTAssertEqual("deflate", response.headers["Content-Encoding"].first) - } - XCTAssertEqual(body, data?.data) - } - } - - func testDecompressionLimit() throws { - let localHTTPBin = HTTPBin(.http1_1(compress: true)) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: .init(decompression: .enabled(limit: .ratio(1))) - ) - - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - - var request = try HTTPClient.Request(url: "http://localhost/(localHTTPBin.port)/post", method: .POST) - request.body = .byteBuffer(ByteBuffer(bytes: [120, 156, 75, 76, 28, 5, 200, 0, 0, 248, 66, 103, 17])) - request.headers.add(name: "Accept-Encoding", value: "deflate") - - XCTAssertThrowsError(try localClient.execute(request: request).wait()) { - XCTAssertEqual($0 as? NIOHTTPDecompression.DecompressionError, .limit) - } - } - - func testLoopDetectionRedirectLimit() throws { - let localHTTPBin = HTTPBin(.http1_1(ssl: true)) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: HTTPClient.Configuration( - certificateVerification: .none, - redirectConfiguration: .follow(max: 5, allowCycles: false) - ) - ) - - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - - XCTAssertThrowsError( - try localClient.get(url: "https://localhost/(localHTTPBin.port)/redirect/infinite1").wait(), - "Should fail with redirect limit" - ) { error in - XCTAssertEqual(error as? HTTPClientError, HTTPClientError.redirectCycleDetected) - } - } - - func testCountRedirectLimit() throws { - let localHTTPBin = HTTPBin(.http1_1(ssl: true)) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: HTTPClient.Configuration( - certificateVerification: .none, - redirectConfiguration: .follow(max: 10, allowCycles: true) - ) - ) - - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - - XCTAssertThrowsError( - try localClient.get(url: "https://localhost/(localHTTPBin.port)/redirect/infinite1").timeout( - after: .seconds(10) - ).wait() - ) { error in - XCTAssertEqual(error as? HTTPClientError, HTTPClientError.redirectLimitReached) - } - } - - func testRedirectToTheInitialURLDoesThrowOnFirstRedirect() throws { - let localHTTPBin = HTTPBin(.http1_1(ssl: true)) - defer { XCTAssertNoThrow(try localHTTPBin.shutdown()) } - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: .init( - certificateVerification: .none, - redirectConfiguration: .follow(max: 1, allowCycles: false) - ) - ) - defer { XCTAssertNoThrow(try localClient.syncShutdown()) } - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow( - maybeRequest = try HTTPClient.Request( - url: "https://localhost/(localHTTPBin.port)/redirect/target", - method: .GET, - headers: [ - "X-Target-Redirect-URL": "/redirect/target" - ] - ) - ) - guard let request = maybeRequest else { return } - - XCTAssertThrowsError( - try localClient.execute(request: request).timeout(after: .seconds(10)).wait() - ) { error in - XCTAssertEqual(error as? HTTPClientError, HTTPClientError.redirectCycleDetected) - } - } - - func testMultipleConcurrentRequests() throws { - let numberOfRequestsPerThread = 1000 - let numberOfParallelWorkers = 5 - - final class HTTPServer: ChannelInboundHandler, Sendable { - typealias InboundIn = HTTPServerRequestPart - typealias OutboundOut = HTTPServerResponsePart - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - if case .end = self.unwrapInboundIn(data) { - let responseHead = HTTPServerResponsePart.head( - .init( - version: .init(major: 1, minor: 1), - status: .ok - ) - ) - context.write(self.wrapOutboundOut(responseHead), promise: nil) - context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: nil) - } - } - } - - let group = MultiThreadedEventLoopGroup(numberOfThreads: 2) - defer { - XCTAssertNoThrow(try group.syncShutdownGracefully()) - } - - var server: Channel? - XCTAssertNoThrow( - server = try ServerBootstrap(group: group) - .serverChannelOption(ChannelOptions.socket(.init(SOL_SOCKET), .init(SO_REUSEADDR)), value: 1) - .serverChannelOption(ChannelOptions.backlog, value: .init(numberOfParallelWorkers)) - .childChannelInitializer { channel in - channel.pipeline.configureHTTPServerPipeline( - withPipeliningAssistance: false, - withServerUpgrade: nil, - withErrorHandling: false - ).flatMap { - channel.pipeline.addHandler(HTTPServer()) - } - } - .bind(to: .init(ipAddress: "127.0.0.1", port: 0)) - .wait() - ) - defer { - XCTAssertNoThrow(try server?.close().wait()) - } - - let url = "http://127.0.0.1/(server?.localAddress?.port%20??%20-1)/hello" - let g = DispatchGroup() - let defaultClient = self.defaultClient! - for workerID in 0.. HTTPClient.Task in - localClient.execute(request: request, delegate: TestHTTPDelegate()) - } - - let results = try EventLoopFuture.whenAllComplete( - tasks.map { $0.futureResult }, - on: localClient.eventLoopGroup.next() - ).wait() - - for result in results { - switch result { - case .success: - XCTFail("Shouldn't succeed") - continue - case .failure(let error): - if isTestingNIOTS() { - #if canImport(Network) - guard let clientError = error as? HTTPClient.NWTLSError else { - XCTFail("Unexpected error: \(error)") - continue - } - // We're speaking TLS to a plain text server. This will cause the handshake to fail but given - // that the bytes "HTTP/1.1" aren't the start of a valid TLS packet, we can also get - // errSSLPeerProtocolVersion because the first bytes contain the version. - XCTAssert( - clientError.status == errSSLHandshakeFail || clientError.status == errSSLPeerProtocolVersion, - "unexpected NWTLSError with status \(clientError.status)" - ) - #endif - } else { - guard let clientError = error as? NIOSSLError, case NIOSSLError.handshakeFailed = clientError else { - XCTFail("Unexpected error: \(error)") - continue - } - } - } - } - } - - func testSelfSignedCertificateIsRejectedWithCorrectError() throws { - /// key + cert was created with the follwing command: - /// openssl req -x509 -newkey rsa:4096 -keyout self_signed_key.pem -out self_signed_cert.pem -sha256 -days 99999 -nodes -subj '/CN=localhost' - let certPath = Bundle.module.path(forResource: "self_signed_cert", ofType: "pem")! - let keyPath = Bundle.module.path(forResource: "self_signed_key", ofType: "pem")! - let key = try NIOSSLPrivateKey(file: keyPath, format: .pem) - let configuration = try TLSConfiguration.makeServerConfiguration( - certificateChain: NIOSSLCertificate.fromPEMFile(certPath).map { .certificate($0) }, - privateKey: .privateKey(key) - ) - let sslContext = try NIOSSLContext(configuration: configuration) - - let server = ServerBootstrap(group: serverGroup) - .childChannelInitializer { channel in - channel.eventLoop.makeCompletedFuture { - try channel.pipeline.syncOperations.addHandler(NIOSSLServerHandler(context: sslContext)) - } - } - let serverChannel = try server.bind(host: "localhost", port: 0).wait() - defer { XCTAssertNoThrow(try serverChannel.close().wait()) } - let port = serverChannel.localAddress!.port! - - let config = HTTPClient.Configuration().enableFastFailureModeForTesting() - - let localClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup), configuration: config) - defer { XCTAssertNoThrow(try localClient.syncShutdown()) } - XCTAssertThrowsError(try localClient.get(url: "https://localhost/(port)").wait()) { error in - #if canImport(Network) - guard let nwTLSError = error as? HTTPClient.NWTLSError else { - XCTFail("could not cast \(error) of type \(type(of: error)) to \(HTTPClient.NWTLSError.self)") - return - } - XCTAssertEqual(nwTLSError.status, errSSLBadCert, "unexpected tls error: \(nwTLSError)") - #else - guard let sslError = error as? NIOSSLError, - case .handshakeFailed(.sslError) = sslError - else { - XCTFail("unexpected error \(error)") - return - } - #endif - } - } - - func testSelfSignedCertificateIsRejectedWithCorrectErrorIfRequestDeadlineIsExceeded() throws { - /// key + cert was created with the follwing command: - /// openssl req -x509 -newkey rsa:4096 -keyout self_signed_key.pem -out self_signed_cert.pem -sha256 -days 99999 -nodes -subj '/CN=localhost' - let certPath = Bundle.module.path(forResource: "self_signed_cert", ofType: "pem")! - let keyPath = Bundle.module.path(forResource: "self_signed_key", ofType: "pem")! - let key = try NIOSSLPrivateKey(file: keyPath, format: .pem) - let configuration = try TLSConfiguration.makeServerConfiguration( - certificateChain: NIOSSLCertificate.fromPEMFile(certPath).map { .certificate($0) }, - privateKey: .privateKey(key) - ) - let sslContext = try NIOSSLContext(configuration: configuration) - - let server = ServerBootstrap(group: serverGroup) - .childChannelInitializer { channel in - channel.eventLoop.makeCompletedFuture { - try channel.pipeline.syncOperations.addHandler(NIOSSLServerHandler(context: sslContext)) - } - } - let serverChannel = try server.bind(host: "localhost", port: 0).wait() - defer { XCTAssertNoThrow(try serverChannel.close().wait()) } - let port = serverChannel.localAddress!.port! - - let config = HTTPClient.Configuration().enableFastFailureModeForTesting() - - let localClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup), configuration: config) - defer { XCTAssertNoThrow(try localClient.syncShutdown()) } - - XCTAssertThrowsError( - try localClient.get(url: "https://localhost/(port)", deadline: .now() + .seconds(2)).wait() - ) { error in - #if canImport(Network) - guard let nwTLSError = error as? HTTPClient.NWTLSError else { - XCTFail("could not cast \(error) of type \(type(of: error)) to \(HTTPClient.NWTLSError.self)") - return - } - XCTAssertEqual(nwTLSError.status, errSSLBadCert, "unexpected tls error: \(nwTLSError)") - #else - guard let sslError = error as? NIOSSLError, - case .handshakeFailed(.sslError) = sslError - else { - XCTFail("unexpected error \(error)") - return - } - #endif - } - } - - func testFailingConnectionIsReleased() { - let localHTTPBin = HTTPBin(.refuse) - let localClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup)) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - do { - _ = try localClient.get(url: "http://localhost/(localHTTPBin.port)/get").timeout(after: .seconds(5)).wait() - XCTFail("Shouldn't succeed") - } catch { - guard !(error is EventLoopFutureTimeoutError) else { - XCTFail("Timed out but should have failed immediately") - return - } - } - } - - func testStressGetClose() throws { - let eventLoop = self.defaultClient.eventLoopGroup.next() - let requestCount = 200 - var futureResults = [EventLoopFuture]() - for _ in 1...requestCount { - let req = try HTTPClient.Request( - url: self.defaultHTTPBinURLPrefix + "get", - method: .GET, - headers: ["X-internal-delay": "5", "Connection": "close"] - ) - futureResults.append(self.defaultClient.execute(request: req)) - } - XCTAssertNoThrow( - try EventLoopFuture.andAllComplete(futureResults, on: eventLoop) - .timeout(after: .seconds(10)).wait() - ) - } - - func testManyConcurrentRequestsWork() { - let numberOfWorkers = 20 - let numberOfRequestsPerWorkers = 20 - let allWorkersReady = DispatchSemaphore(value: 0) - let allWorkersGo = DispatchSemaphore(value: 0) - let allDone = DispatchGroup() - - let url = self.defaultHTTPBinURLPrefix + "get" - XCTAssertEqual(.ok, try self.defaultClient.get(url: url).wait().status) - - for w in 0..]() - for i in 1...100 { - let request = try HTTPClient.Request( - url: self.defaultHTTPBinURLPrefix + "get", - method: .GET, - headers: ["X-internal-delay": "10"] - ) - let preference: HTTPClient.EventLoopPreference - if i <= 50 { - preference = .delegateAndChannel(on: first) - } else { - preference = .delegateAndChannel(on: second) - } - futureResults.append(client.execute(request: request, eventLoop: preference)) - } - - let results = try EventLoopFuture.whenAllComplete(futureResults, on: elg.next()).wait() - - for result in results { - switch result { - case .success: - break - case .failure(let error): - XCTFail("Unexpected error: \(error)") - } - } - } - - func testMakeSecondRequestDuringCancelledCallout() { - let el = self.clientGroup.next() - let web = NIOHTTP1TestServer(group: self.serverGroup.next()) - defer { - // This will throw as we've started the request but haven't fulfilled it. - XCTAssertThrowsError(try web.stop()) - } - - let url = "http://127.0.0.1/(web.serverPort)" - let localClient = HTTPClient(eventLoopGroupProvider: .shared(el)) - defer { - XCTAssertThrowsError(try localClient.syncShutdown()) { error in - XCTAssertEqual(.alreadyShutdown, error as? HTTPClientError) - } - } - - let seenError = DispatchGroup() - seenError.enter() - var maybeSecondRequest: EventLoopFuture? - XCTAssertNoThrow( - maybeSecondRequest = try el.submit { - let neverSucceedingRequest = localClient.get(url: url) - let secondRequest = neverSucceedingRequest.flatMapError { error in - XCTAssertEqual(.cancelled, error as? HTTPClientError) - seenError.leave() - // v this is the main part, during the error callout, we call back in - return localClient.get(url: url) - } - return secondRequest - }.wait() - ) - - guard let secondRequest = maybeSecondRequest else { - XCTFail("couldn't get request future") - return - } - - // Let's pull out the request .head so we know the request has started (but nothing else) - XCTAssertNoThrow(XCTAssertNotNil(try web.readInbound())) - - XCTAssertNoThrow(try localClient.syncShutdown()) - - seenError.wait() - XCTAssertThrowsError(try secondRequest.wait()) { error in - XCTAssertEqual(.alreadyShutdown, error as? HTTPClientError) - } - } - - func testMakeSecondRequestDuringSuccessCallout() { - let el = self.clientGroup.next() - let url = "http://127.0.0.1/(self.defaultHTTPBin.port)/get" - let localClient = HTTPClient(eventLoopGroupProvider: .shared(el)) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - } - - XCTAssertEqual( - .ok, - try el.flatSubmit { () -> EventLoopFuture in - localClient.get(url: url).flatMap { firstResponse in - XCTAssertEqual(.ok, firstResponse.status) - return localClient.get(url: url) // <== interesting bit here - } - }.wait().status - ) - } - - func testMakeSecondRequestWhilstFirstIsOngoing() { - let web = NIOHTTP1TestServer(group: self.serverGroup) - defer { - XCTAssertNoThrow(try web.stop()) - } - - let client = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup)) - defer { - XCTAssertNoThrow(try client.syncShutdown()) - } - - let url = "http://127.0.0.1/(web.serverPort)" - let firstRequest = client.get(url: url) - - XCTAssertNoThrow(XCTAssertNotNil(try web.readInbound())) // first request: .head - - // Now, the first request is ongoing but not complete, let's start a second one - let secondRequest = client.get(url: url) - XCTAssertEqual(.end(nil), try web.readInbound()) // first request: .end - - XCTAssertNoThrow(try web.writeOutbound(.head(.init(version: .init(major: 1, minor: 1), status: .ok)))) - XCTAssertNoThrow(try web.writeOutbound(.end(nil))) - - XCTAssertEqual(.ok, try firstRequest.wait().status) - - // Okay, first request done successfully, let's do the second one too. - XCTAssertNoThrow(XCTAssertNotNil(try web.readInbound())) // first request: .head - XCTAssertEqual(.end(nil), try web.readInbound()) // first request: .end - - XCTAssertNoThrow(try web.writeOutbound(.head(.init(version: .init(major: 1, minor: 1), status: .created)))) - XCTAssertNoThrow(try web.writeOutbound(.end(nil))) - XCTAssertEqual(.created, try secondRequest.wait().status) - } - - func testUDSBasic() { - // This tests just connecting to a URL where the whole URL is the UNIX domain socket path like - // unix:///this/is/my/socket.sock - // We don't really have a path component, so we'll have to use "/" - XCTAssertNoThrow( - try TemporaryFileHelpers.withTemporaryUnixDomainSocketPathName { path in - let localHTTPBin = HTTPBin(bindTarget: .unixDomainSocket(path)) - defer { - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - let target = "unix://\(path)" - XCTAssertEqual( - ["Yes"[...]], - try self.defaultClient.get(url: target).wait().headers[canonicalForm: "X-Is-This-Slash"] - ) - } - ) - } - - func testUDSSocketAndPath() { - // Here, we're testing a URL that's encoding two different paths: - // - // 1. a "base path" which is the path to the UNIX domain socket - // 2. an actual path which is the normal path in a regular URL like https://example.com/this/is/the/path - XCTAssertNoThrow( - try TemporaryFileHelpers.withTemporaryUnixDomainSocketPathName { path in - let localHTTPBin = HTTPBin(bindTarget: .unixDomainSocket(path)) - defer { - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - guard let target = URL(string: "/echo-uri", relativeTo: URL(string: "unix://\(path)")), - let request = try? Request(url: target) - else { - XCTFail("couldn't build URL for request") - return - } - XCTAssertEqual( - ["/echo-uri"[...]], - try self.defaultClient.execute(request: request).wait().headers[canonicalForm: "X-Calling-URI"] - ) - } - ) - } - - func testHTTPPlusUNIX() { - // Here, we're testing a URL where the UNIX domain socket is encoded as the host name - XCTAssertNoThrow( - try TemporaryFileHelpers.withTemporaryUnixDomainSocketPathName { path in - let localHTTPBin = HTTPBin(bindTarget: .unixDomainSocket(path)) - defer { - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - guard let target = URL(httpURLWithSocketPath: path, uri: "/echo-uri"), - let request = try? Request(url: target) - else { - XCTFail("couldn't build URL for request") - return - } - XCTAssertEqual( - ["/echo-uri"[...]], - try self.defaultClient.execute(request: request).wait().headers[canonicalForm: "X-Calling-URI"] - ) - } - ) - } - - func testHTTPSPlusUNIX() { - // Here, we're testing a URL where the UNIX domain socket is encoded as the host name - XCTAssertNoThrow( - try TemporaryFileHelpers.withTemporaryUnixDomainSocketPathName { path in - let localHTTPBin = HTTPBin(.http1_1(ssl: true), bindTarget: .unixDomainSocket(path)) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: HTTPClient.Configuration(certificateVerification: .none) - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - guard let target = URL(httpsURLWithSocketPath: path, uri: "/echo-uri"), - let request = try? Request(url: target) - else { - XCTFail("couldn't build URL for request") - return - } - XCTAssertEqual( - ["/echo-uri"[...]], - try localClient.execute(request: request).wait().headers[canonicalForm: "X-Calling-URI"] - ) - } - ) - } - - func testUseExistingConnectionOnDifferentEL() throws { - let threadCount = 16 - let elg = getDefaultEventLoopGroup(numberOfThreads: threadCount) - let localClient = HTTPClient(eventLoopGroupProvider: .shared(elg)) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try elg.syncShutdownGracefully()) - } - - let eventLoops = (1...threadCount).map { _ in elg.next() } - let request = try HTTPClient.Request(url: self.defaultHTTPBinURLPrefix + "get") - let closingRequest = try HTTPClient.Request( - url: self.defaultHTTPBinURLPrefix + "get", - headers: ["Connection": "close"] - ) - - for (index, el) in eventLoops.enumerated() { - if index.isMultiple(of: 2) { - XCTAssertNoThrow( - try localClient.execute(request: request, eventLoop: .delegateAndChannel(on: el)).wait() - ) - } else { - XCTAssertNoThrow( - try localClient.execute(request: request, eventLoop: .delegateAndChannel(on: el)).wait() - ) - XCTAssertNoThrow(try localClient.execute(request: closingRequest, eventLoop: .indifferent).wait()) - } - } - } - - func testWeRecoverFromServerThatClosesTheConnectionOnUs() { - final class ServerThatAcceptsThenRejects: ChannelInboundHandler, Sendable { - typealias InboundIn = HTTPServerRequestPart - typealias OutboundOut = HTTPServerResponsePart - - let requestNumber: ManagedAtomic - let connectionNumber: ManagedAtomic - - init(requestNumber: ManagedAtomic, connectionNumber: ManagedAtomic) { - self.requestNumber = requestNumber - self.connectionNumber = connectionNumber - } - - func channelActive(context: ChannelHandlerContext) { - _ = self.connectionNumber.loadThenWrappingIncrement(ordering: .relaxed) - } - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - let req = self.unwrapInboundIn(data) - - switch req { - case .head, .body: - () - case .end: - let last = self.requestNumber.loadThenWrappingIncrement(ordering: .relaxed) - switch last { - case 0, 2: - context.write( - self.wrapOutboundOut(.head(.init(version: .init(major: 1, minor: 1), status: .ok))), - promise: nil - ) - context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: nil) - case 1: - context.close(promise: nil) - default: - XCTFail("did not expect request \(last + 1)") - } - } - } - } - - let requestNumber = ManagedAtomic(0) - let connectionNumber = ManagedAtomic(0) - let sharedStateServerHandler = ServerThatAcceptsThenRejects( - requestNumber: requestNumber, - connectionNumber: connectionNumber - ) - var maybeServer: Channel? - XCTAssertNoThrow( - maybeServer = try ServerBootstrap(group: self.serverGroup) - .serverChannelOption(ChannelOptions.socket(.init(SOL_SOCKET), .init(SO_REUSEADDR)), value: 1) - .childChannelInitializer { channel in - channel.pipeline.configureHTTPServerPipeline().flatMap { - // We're deliberately adding a handler which is shared between multiple channels. This is normally - // very verboten but this handler is specially crafted to tolerate this. - channel.pipeline.addHandler(sharedStateServerHandler) - } - } - .bind(host: "127.0.0.1", port: 0) - .wait() - ) - guard let server = maybeServer else { - XCTFail("couldn't create server") - return - } - defer { - XCTAssertNoThrow(try server.close().wait()) - } - - let url = "http://127.0.0.1/(server.localAddress!.port!)" - let client = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup)) - defer { - XCTAssertNoThrow(try client.syncShutdown()) - } - - XCTAssertEqual(0, sharedStateServerHandler.connectionNumber.load(ordering: .relaxed)) - XCTAssertEqual(0, sharedStateServerHandler.requestNumber.load(ordering: .relaxed)) - XCTAssertEqual(.ok, try client.get(url: url).wait().status) - XCTAssertEqual(1, sharedStateServerHandler.connectionNumber.load(ordering: .relaxed)) - XCTAssertEqual(1, sharedStateServerHandler.requestNumber.load(ordering: .relaxed)) - XCTAssertThrowsError(try client.get(url: url).wait().status) { error in - XCTAssertEqual(.remoteConnectionClosed, error as? HTTPClientError) - } - XCTAssertEqual(1, sharedStateServerHandler.connectionNumber.load(ordering: .relaxed)) - XCTAssertEqual(2, sharedStateServerHandler.requestNumber.load(ordering: .relaxed)) - XCTAssertEqual(.ok, try client.get(url: url).wait().status) - XCTAssertEqual(2, sharedStateServerHandler.connectionNumber.load(ordering: .relaxed)) - XCTAssertEqual(3, sharedStateServerHandler.requestNumber.load(ordering: .relaxed)) - } - - func testPoolClosesIdleConnections() { - let configuration = HTTPClient.Configuration( - certificateVerification: .none, - maximumAllowedIdleTimeInConnectionPool: .milliseconds(100) - ) - - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: configuration - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - } - - // Make sure that the idle timeout of the connection pool is properly propagated - // to the connection pool itself, when using both inits. - XCTAssertEqual(configuration.connectionPool.idleTimeout, .milliseconds(100)) - XCTAssertEqual( - configuration.connectionPool.idleTimeout, - HTTPClient.Configuration( - certificateVerification: .none, - connectionPool: .milliseconds(100), - backgroundActivityLogger: nil - ).connectionPool.idleTimeout - ) - - XCTAssertNoThrow(try localClient.get(url: self.defaultHTTPBinURLPrefix + "get").wait()) - Thread.sleep(forTimeInterval: 0.2) - XCTAssertEqual(self.defaultHTTPBin.activeConnections, 0) - } - - func testAvoidLeakingTLSHandshakeCompletionPromise() { - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: .init(timeout: .init(connect: .milliseconds(100))) - ) - let localHTTPBin = HTTPBin() - let port = localHTTPBin.port - XCTAssertNoThrow(try localHTTPBin.shutdown()) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - } - - XCTAssertThrowsError(try localClient.get(url: "http://localhost/(port)").wait()) { error in - if isTestingNIOTS() { - #if canImport(Network) - // We can't be more specific than this. - XCTAssertTrue(error is HTTPClient.NWTLSError || error is HTTPClient.NWPOSIXError) - #else - XCTFail("Impossible condition") - #endif - } else { - XCTAssert(error is NIOConnectionError, "Unexpected error: \(error)") - } - } - } - - func testAsyncShutdown() throws { - let localClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup)) - let promise = self.clientGroup.next().makePromise(of: Void.self) - self.clientGroup.next().execute { - localClient.shutdown(queue: DispatchQueue(label: "testAsyncShutdown")) { error in - XCTAssertNil(error) - promise.succeed(()) - } - } - XCTAssertNoThrow(try promise.futureResult.wait()) - } - - func testAsyncShutdownDefaultQueue() throws { - let localClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup)) - let promise = self.clientGroup.next().makePromise(of: Void.self) - self.clientGroup.next().execute { - localClient.shutdown { error in - XCTAssertNil(error) - promise.succeed(()) - } - } - XCTAssertNoThrow(try promise.futureResult.wait()) - } - - func testValidationErrorsAreSurfaced() throws { - let defaultClient = self.defaultClient! - let request = try HTTPClient.Request( - url: self.defaultHTTPBinURLPrefix + "get", - method: .TRACE, - body: .stream { _ in - defaultClient.eventLoopGroup.next().makeSucceededFuture(()) - } - ) - let runningRequest = self.defaultClient.execute(request: request) - XCTAssertThrowsError(try runningRequest.wait()) { error in - XCTAssertEqual(HTTPClientError.traceRequestWithBody, error as? HTTPClientError) - } - } - - func testUploadsReallyStream() { - final class HTTPServer: ChannelInboundHandler { - typealias InboundIn = HTTPServerRequestPart - typealias OutboundOut = HTTPServerResponsePart - - private let headPromise: EventLoopPromise - private let bodyPromises: [EventLoopPromise] - private let endPromise: EventLoopPromise - private var bodyPartsSeenSoFar = 0 - private var atEnd = false - - init( - headPromise: EventLoopPromise, - bodyPromises: [EventLoopPromise], - endPromise: EventLoopPromise - ) { - self.headPromise = headPromise - self.bodyPromises = bodyPromises - self.endPromise = endPromise - } - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - switch self.unwrapInboundIn(data) { - case .head(let head): - XCTAssert(self.bodyPartsSeenSoFar == 0) - self.headPromise.succeed(head) - case .body(let bytes): - let myNumber = self.bodyPartsSeenSoFar - self.bodyPartsSeenSoFar += 1 - self.bodyPromises.dropFirst(myNumber).first?.succeed(bytes) ?? XCTFail("ouch, too many chunks") - case .end: - context.write( - self.wrapOutboundOut(.head(.init(version: .init(major: 1, minor: 1), status: .ok))), - promise: nil - ) - context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: self.endPromise) - self.atEnd = true - } - } - - func handlerRemoved(context: ChannelHandlerContext) { - guard !self.atEnd else { - return - } - struct NotFulfilledError: Error {} - - self.headPromise.fail(NotFulfilledError()) - for promise in self.bodyPromises { - promise.fail(NotFulfilledError()) - } - self.endPromise.fail(NotFulfilledError()) - } - } - - let group = MultiThreadedEventLoopGroup(numberOfThreads: 2) - defer { - XCTAssertNoThrow(try group.syncShutdownGracefully()) - } - let client = HTTPClient(eventLoopGroupProvider: .shared(group)) - defer { - XCTAssertNoThrow(try client.syncShutdown()) - } - let headPromise = group.next().makePromise(of: HTTPRequestHead.self) - let bodyPromises = (0..<16).map { _ in group.next().makePromise(of: ByteBuffer.self) } - let endPromise = group.next().makePromise(of: Void.self) - let sentOffAllBodyPartsPromise = group.next().makePromise(of: Void.self) - let streamWriterPromise = group.next().makePromise(of: HTTPClient.Body.StreamWriter.self) - - func makeServer() -> Channel? { - try? ServerBootstrap(group: group) - .childChannelInitializer { channel in - channel.pipeline.configureHTTPServerPipeline().flatMapThrowing { - try channel.pipeline.syncOperations.addHandler( - HTTPServer( - headPromise: headPromise, - bodyPromises: bodyPromises, - endPromise: endPromise - ) - ) - } - } - .serverChannelOption(ChannelOptions.socket(.init(SOL_SOCKET), .init(SO_REUSEADDR)), value: 1) - .bind(host: "127.0.0.1", port: 0) - .wait() - } - - func makeRequest(server: Channel) -> Request? { - guard let localAddress = server.localAddress else { - return nil - } - - return try? HTTPClient.Request( - url: "http://(localaddress.ipaddress!)/(localAddress.port!)", - method: .POST, - headers: ["transfer-encoding": "chunked"], - body: .stream { streamWriter in - streamWriterPromise.succeed(streamWriter) - return sentOffAllBodyPartsPromise.futureResult - } - ) - } - - guard let server = makeServer(), let request = makeRequest(server: server) else { - XCTFail("couldn't make a server Channel and a matching Request...") - return - } - defer { - XCTAssertNoThrow(try server.close().wait()) - } - - var buffer = ByteBufferAllocator().buffer(capacity: 1) - let runningRequest = client.execute(request: request) - guard let streamWriter = try? streamWriterPromise.futureResult.wait() else { - XCTFail("didn't get StreamWriter") - return - } - - XCTAssertEqual(.POST, try headPromise.futureResult.wait().method) - for bodyChunkNumber in 0..<16 { - buffer.clear() - buffer.writeString(String(bodyChunkNumber, radix: 16)) - XCTAssertEqual(1, buffer.readableBytes) - XCTAssertNoThrow(try streamWriter.write(.byteBuffer(buffer)).wait()) - XCTAssertEqual(buffer, try bodyPromises[bodyChunkNumber].futureResult.wait()) - } - sentOffAllBodyPartsPromise.succeed(()) - XCTAssertNoThrow(try endPromise.futureResult.wait()) - XCTAssertNoThrow(try runningRequest.wait()) - } - - func testUploadStreamingCallinToleratedFromOtsideEL() throws { - let defaultClient = self.defaultClient! - let request = try HTTPClient.Request( - url: self.defaultHTTPBinURLPrefix + "get", - method: .POST, - body: .stream(contentLength: 4) { writer in - let promise = defaultClient.eventLoopGroup.next().makePromise(of: Void.self) - // We have to toleare callins from any thread - DispatchQueue(label: "upload-streaming").async { - writer.write(.byteBuffer(ByteBuffer(string: "1234"))).whenComplete { _ in - promise.succeed(()) - } - } - return promise.futureResult - } - ) - XCTAssertNoThrow(try self.defaultClient.execute(request: request).wait()) - } - - func testWeHandleUsSendingACloseHeaderCorrectly() { - guard - let req1 = try? Request( - url: self.defaultHTTPBinURLPrefix + "stats", - method: .GET, - headers: ["connection": "close"] - ), - let statsBytes1 = try? self.defaultClient.execute(request: req1).wait().body, - let stats1 = try? JSONDecoder().decode(RequestInfo.self, from: statsBytes1) - else { - XCTFail("request 1 didn't work") - return - } - guard let statsBytes2 = try? self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "stats").wait().body, - let stats2 = try? JSONDecoder().decode(RequestInfo.self, from: statsBytes2) - else { - XCTFail("request 2 didn't work") - return - } - guard let statsBytes3 = try? self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "stats").wait().body, - let stats3 = try? JSONDecoder().decode(RequestInfo.self, from: statsBytes3) - else { - XCTFail("request 3 didn't work") - return - } - - // req 1 and 2 cannot share the same connection (close header) - XCTAssertEqual(stats1.connectionNumber + 1, stats2.connectionNumber) - XCTAssertEqual(stats1.requestNumber, 1) - XCTAssertEqual(stats2.requestNumber, 1) - - // req 2 and 3 should share the same connection (keep-alive is default) - XCTAssertEqual(stats2.requestNumber + 1, stats3.requestNumber) - XCTAssertEqual(stats2.connectionNumber, stats3.connectionNumber) - } - - func testWeHandleUsReceivingACloseHeaderCorrectly() { - guard - let req1 = try? Request( - url: self.defaultHTTPBinURLPrefix + "stats", - method: .GET, - headers: ["X-Send-Back-Header-Connection": "close"] - ), - let statsBytes1 = try? self.defaultClient.execute(request: req1).wait().body, - let stats1 = try? JSONDecoder().decode(RequestInfo.self, from: statsBytes1) - else { - XCTFail("request 1 didn't work") - return - } - guard let statsBytes2 = try? self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "stats").wait().body, - let stats2 = try? JSONDecoder().decode(RequestInfo.self, from: statsBytes2) - else { - XCTFail("request 2 didn't work") - return - } - guard let statsBytes3 = try? self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "stats").wait().body, - let stats3 = try? JSONDecoder().decode(RequestInfo.self, from: statsBytes3) - else { - XCTFail("request 3 didn't work") - return - } - - // req 1 and 2 cannot share the same connection (close header) - XCTAssertEqual(stats1.connectionNumber + 1, stats2.connectionNumber) - XCTAssertEqual(stats1.requestNumber, 1) - XCTAssertEqual(stats2.requestNumber, 1) - - // req 2 and 3 should share the same connection (keep-alive is default) - XCTAssertEqual(stats2.requestNumber + 1, stats3.requestNumber) - XCTAssertEqual(stats2.connectionNumber, stats3.connectionNumber) - } - - func testWeHandleUsSendingACloseHeaderAmongstOtherConnectionHeadersCorrectly() { - for closeHeader in [("connection", "close"), ("CoNneCTION", "ClOSe")] { - guard - let req1 = try? Request( - url: self.defaultHTTPBinURLPrefix + "stats", - method: .GET, - headers: [ - "X-Send-Back-Header-\(closeHeader.0)": - "foo,\(closeHeader.1),bar" - ] - ), - let statsBytes1 = try? self.defaultClient.execute(request: req1).wait().body, - let stats1 = try? JSONDecoder().decode(RequestInfo.self, from: statsBytes1) - else { - XCTFail("request 1 didn't work") - return - } - guard - let statsBytes2 = try? self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "stats").wait().body, - let stats2 = try? JSONDecoder().decode(RequestInfo.self, from: statsBytes2) - else { - XCTFail("request 2 didn't work") - return - } - guard - let statsBytes3 = try? self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "stats").wait().body, - let stats3 = try? JSONDecoder().decode(RequestInfo.self, from: statsBytes3) - else { - XCTFail("request 3 didn't work") - return - } - - // req 1 and 2 cannot share the same connection (close header) - XCTAssertEqual(stats1.connectionNumber + 1, stats2.connectionNumber) - XCTAssertEqual(stats2.requestNumber, 1) - - // req 2 and 3 should share the same connection (keep-alive is default) - XCTAssertEqual(stats2.requestNumber + 1, stats3.requestNumber) - XCTAssertEqual(stats2.connectionNumber, stats3.connectionNumber) - } - } - - func testWeHandleUsReceivingACloseHeaderAmongstOtherConnectionHeadersCorrectly() { - for closeHeader in [("connection", "close"), ("CoNneCTION", "ClOSe")] { - guard - let req1 = try? Request( - url: self.defaultHTTPBinURLPrefix + "stats", - method: .GET, - headers: [ - "X-Send-Back-Header-\(closeHeader.0)": - "foo,\(closeHeader.1),bar" - ] - ), - let statsBytes1 = try? self.defaultClient.execute(request: req1).wait().body, - let stats1 = try? JSONDecoder().decode(RequestInfo.self, from: statsBytes1) - else { - XCTFail("request 1 didn't work") - return - } - guard - let statsBytes2 = try? self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "stats").wait().body, - let stats2 = try? JSONDecoder().decode(RequestInfo.self, from: statsBytes2) - else { - XCTFail("request 2 didn't work") - return - } - guard - let statsBytes3 = try? self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "stats").wait().body, - let stats3 = try? JSONDecoder().decode(RequestInfo.self, from: statsBytes3) - else { - XCTFail("request 3 didn't work") - return - } - - // req 1 and 2 cannot share the same connection (close header) - XCTAssertEqual(stats1.connectionNumber + 1, stats2.connectionNumber) - XCTAssertEqual(stats2.requestNumber, 1) - - // req 2 and 3 should share the same connection (keep-alive is default) - XCTAssertEqual(stats2.requestNumber + 1, stats3.requestNumber) - XCTAssertEqual(stats2.connectionNumber, stats3.connectionNumber) - } - } - - func testLoggingCorrectlyAttachesRequestInformationEvenAfterDuringRedirect() { - let logStore = CollectEverythingLogHandler.LogStore() - - var logger = Logger( - label: "\(#function)", - factory: { _ in - CollectEverythingLogHandler(logStore: logStore) - } - ) - logger.logLevel = .trace - logger[metadataKey: "custom-request-id"] = "abcd" - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow( - maybeRequest = try HTTPClient.Request( - url: "http://localhost/(self.defaultHTTPBin.port)/redirect/target", - method: .GET, - headers: [ - "X-Target-Redirect-URL": "/get" - ] - ) - ) - guard let request = maybeRequest else { return } - - XCTAssertNoThrow( - try self.defaultClient.execute( - request: request, - eventLoop: .indifferent, - deadline: nil, - logger: logger - ).wait() - ) - let logs = logStore.allEntries - - XCTAssertTrue(logs.allSatisfy { $0.metadata["custom-request-id"] == "abcd" }) - - guard let firstRequestID = logs.first?.metadata["ahc-request-id"] else { - return XCTFail("could not get first request ID") - } - guard let lastRequestID = logs.last?.metadata["ahc-request-id"] else { - return XCTFail("could not get second request ID") - } - - let firstRequestLogs = logs.prefix(while: { $0.metadata["ahc-request-id"] == firstRequestID }) - XCTAssertGreaterThan(firstRequestLogs.count, 0) - - let secondRequestLogs = logs.drop(while: { $0.metadata["ahc-request-id"] == firstRequestID }) - XCTAssertGreaterThan(secondRequestLogs.count, 0) - XCTAssertTrue(secondRequestLogs.allSatisfy { $0.metadata["ahc-request-id"] == lastRequestID }) - - for log in logs { print(log) } - } - - func testLoggingCorrectlyAttachesRequestInformation() { - let logStore = CollectEverythingLogHandler.LogStore() - - var loggerYolo001 = Logger( - label: "\(#function)", - factory: { _ in - CollectEverythingLogHandler(logStore: logStore) - } - ) - loggerYolo001.logLevel = .trace - loggerYolo001[metadataKey: "yolo-request-id"] = "yolo-001" - var loggerACME002 = Logger( - label: "\(#function)", - factory: { _ in - CollectEverythingLogHandler(logStore: logStore) - } - ) - loggerACME002.logLevel = .trace - loggerACME002[metadataKey: "acme-request-id"] = "acme-002" - - guard let request1 = try? HTTPClient.Request(url: self.defaultHTTPBinURLPrefix + "get"), - let request2 = try? HTTPClient.Request(url: self.defaultHTTPBinURLPrefix + "stats"), - let request3 = try? HTTPClient.Request(url: self.defaultHTTPBinURLPrefix + "ok") - else { - XCTFail("bad stuff, can't even make request structures") - return - } - - // === Request 1 (Yolo001) - XCTAssertNoThrow( - try self.defaultClient.execute( - request: request1, - eventLoop: .indifferent, - deadline: nil, - logger: loggerYolo001 - ).wait() - ) - let logsAfterReq1 = logStore.allEntries - logStore.allEntries = [] - - // === Request 2 (Yolo001) - XCTAssertNoThrow( - try self.defaultClient.execute( - request: request2, - eventLoop: .indifferent, - deadline: nil, - logger: loggerYolo001 - ).wait() - ) - let logsAfterReq2 = logStore.allEntries - logStore.allEntries = [] - - // === Request 3 (ACME002) - XCTAssertNoThrow( - try self.defaultClient.execute( - request: request3, - eventLoop: .indifferent, - deadline: nil, - logger: loggerACME002 - ).wait() - ) - let logsAfterReq3 = logStore.allEntries - logStore.allEntries = [] - - // === Assertions - XCTAssertGreaterThan(logsAfterReq1.count, 0) - XCTAssertGreaterThan(logsAfterReq2.count, 0) - XCTAssertGreaterThan(logsAfterReq3.count, 0) - - XCTAssert( - logsAfterReq1.allSatisfy { entry in - if let httpRequestMetadata = entry.metadata["ahc-request-id"], - let yoloRequestID = entry.metadata["yolo-request-id"] - { - XCTAssertNil(entry.metadata["acme-request-id"]) - XCTAssertEqual("yolo-001", yoloRequestID) - XCTAssertNotNil(Int(httpRequestMetadata)) - return true - } else { - XCTFail("log message doesn't contain the right IDs: \(entry)") - return false - } - } - ) - XCTAssert( - logsAfterReq1.contains { entry in - // Since a new connection must be created first we expect that the request is queued - // and log message describing this is emitted. - entry.message == "Request was queued (waiting for a connection to become available)" - && entry.level == .debug - } - ) - XCTAssert( - logsAfterReq1.contains { entry in - // After the new connection was created we expect a log message that describes that the - // request was scheduled on a connection. The connection id must be set from here on. - entry.message == "Request was scheduled on connection" - && entry.level == .debug - && entry.metadata["ahc-connection-id"] != nil - } - ) - - XCTAssert( - logsAfterReq2.allSatisfy { entry in - if let httpRequestMetadata = entry.metadata["ahc-request-id"], - let yoloRequestID = entry.metadata["yolo-request-id"] - { - XCTAssertNil(entry.metadata["acme-request-id"]) - XCTAssertEqual("yolo-001", yoloRequestID) - XCTAssertNotNil(Int(httpRequestMetadata)) - return true - } else { - XCTFail("log message doesn't contain the right IDs: \(entry)") - return false - } - } - ) - XCTAssertFalse( - logsAfterReq2.contains { entry in - entry.message == "Request was queued (waiting for a connection to become available)" - } - ) - XCTAssert( - logsAfterReq2.contains { entry in - entry.message == "Request was scheduled on connection" - && entry.level == .debug - && entry.metadata["ahc-connection-id"] != nil - } - ) - - XCTAssert( - logsAfterReq3.allSatisfy { entry in - if let httpRequestMetadata = entry.metadata["ahc-request-id"], - let acmeRequestID = entry.metadata["acme-request-id"] - { - XCTAssertNil(entry.metadata["yolo-request-id"]) - XCTAssertEqual("acme-002", acmeRequestID) - XCTAssertNotNil(Int(httpRequestMetadata)) - return true - } else { - XCTFail("log message doesn't contain the right IDs: \(entry)") - return false - } - } - ) - XCTAssertFalse( - logsAfterReq3.contains { entry in - entry.message == "Request was queued (waiting for a connection to become available)" - } - ) - XCTAssert( - logsAfterReq3.contains { entry in - entry.message == "Request was scheduled on connection" - && entry.level == .debug - && entry.metadata["ahc-connection-id"] != nil - } - ) - } - - func testNothingIsLoggedAtInfoOrHigher() { - let logStore = CollectEverythingLogHandler.LogStore() - - var logger = Logger( - label: "\(#function)", - factory: { _ in - CollectEverythingLogHandler(logStore: logStore) - } - ) - logger.logLevel = .info - - guard let request1 = try? HTTPClient.Request(url: self.defaultHTTPBinURLPrefix + "get"), - let request2 = try? HTTPClient.Request(url: self.defaultHTTPBinURLPrefix + "stats") - else { - XCTFail("bad stuff, can't even make request structures") - return - } - - // === Request 1 - XCTAssertNoThrow( - try self.defaultClient.execute( - request: request1, - eventLoop: .indifferent, - deadline: nil, - logger: logger - ).wait() - ) - XCTAssertEqual(0, logStore.allEntries.count) - - // === Request 2 - XCTAssertNoThrow( - try self.defaultClient.execute( - request: request2, - eventLoop: .indifferent, - deadline: nil, - logger: logger - ).wait() - ) - XCTAssertEqual(0, logStore.allEntries.count) - - // === Synthesized Request - XCTAssertNoThrow( - try self.defaultClient.execute( - .GET, - url: self.defaultHTTPBinURLPrefix + "get", - body: nil, - deadline: nil, - logger: logger - ).wait() - ) - XCTAssertEqual(0, logStore.allEntries.count) - - XCTAssertEqual(0, self.backgroundLogStore.allEntries.filter { $0.level >= .info }.count) - - // === Synthesized Socket Path Request - XCTAssertNoThrow( - try TemporaryFileHelpers.withTemporaryUnixDomainSocketPathName { path in - let backgroundLogStore = CollectEverythingLogHandler.LogStore() - var backgroundLogger = Logger( - label: "\(#function)", - factory: { _ in - CollectEverythingLogHandler(logStore: backgroundLogStore) - } - ) - backgroundLogger.logLevel = .trace - - let localSocketPathHTTPBin = HTTPBin(bindTarget: .unixDomainSocket(path)) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - backgroundActivityLogger: backgroundLogger - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localSocketPathHTTPBin.shutdown()) - } - - XCTAssertNoThrow( - try localClient.execute( - .GET, - socketPath: path, - urlPath: "get", - body: nil, - deadline: nil, - logger: logger - ).wait() - ) - XCTAssertEqual(0, logStore.allEntries.count) - - XCTAssertEqual(0, backgroundLogStore.allEntries.filter { $0.level >= .info }.count) - } - ) - - // === Synthesized Secure Socket Path Request - XCTAssertNoThrow( - try TemporaryFileHelpers.withTemporaryUnixDomainSocketPathName { path in - let backgroundLogStore = CollectEverythingLogHandler.LogStore() - var backgroundLogger = Logger( - label: "\(#function)", - factory: { _ in - CollectEverythingLogHandler(logStore: backgroundLogStore) - } - ) - backgroundLogger.logLevel = .trace - - let localSocketPathHTTPBin = HTTPBin(.http1_1(ssl: true), bindTarget: .unixDomainSocket(path)) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: HTTPClient.Configuration(certificateVerification: .none), - backgroundActivityLogger: backgroundLogger - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localSocketPathHTTPBin.shutdown()) - } - - XCTAssertNoThrow( - try localClient.execute( - .GET, - secureSocketPath: path, - urlPath: "get", - body: nil, - deadline: nil, - logger: logger - ).wait() - ) - XCTAssertEqual(0, logStore.allEntries.count) - - XCTAssertEqual(0, backgroundLogStore.allEntries.filter { $0.level >= .info }.count) - } - ) - } - - func testAllMethodsLog() { - func checkExpectationsWithLogger(type: String, _ body: (Logger, String) throws -> T) throws -> T { - let logStore = CollectEverythingLogHandler.LogStore() - - var logger = Logger( - label: "\(#function)", - factory: { _ in - CollectEverythingLogHandler(logStore: logStore) - } - ) - logger.logLevel = .trace - logger[metadataKey: "req"] = "yo-\(type)" - - let url = "not-found/request/\(type))" - let result = try body(logger, url) - - XCTAssertGreaterThan(logStore.allEntries.count, 0) - for entry in logStore.allEntries { - XCTAssertEqual("yo-\(type)", entry.metadata["req"] ?? "n/a") - XCTAssertNotNil(Int(entry.metadata["ahc-request-id"] ?? "n/a")) - } - return result - } - - XCTAssertEqual( - .notFound, - try checkExpectationsWithLogger(type: "GET") { logger, url in - try self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + url, logger: logger).wait() - }.status - ) - - XCTAssertEqual( - .notFound, - try checkExpectationsWithLogger(type: "PUT") { logger, url in - try self.defaultClient.put(url: self.defaultHTTPBinURLPrefix + url, logger: logger).wait() - }.status - ) - - XCTAssertEqual( - .notFound, - try checkExpectationsWithLogger(type: "POST") { logger, url in - try self.defaultClient.post(url: self.defaultHTTPBinURLPrefix + url, logger: logger).wait() - }.status - ) - - XCTAssertEqual( - .notFound, - try checkExpectationsWithLogger(type: "DELETE") { logger, url in - try self.defaultClient.delete(url: self.defaultHTTPBinURLPrefix + url, logger: logger).wait() - }.status - ) - - XCTAssertEqual( - .notFound, - try checkExpectationsWithLogger(type: "PATCH") { logger, url in - try self.defaultClient.patch(url: self.defaultHTTPBinURLPrefix + url, logger: logger).wait() - }.status - ) - - XCTAssertEqual( - .notFound, - try checkExpectationsWithLogger(type: "CHECKOUT") { logger, url in - try self.defaultClient.execute(.CHECKOUT, url: self.defaultHTTPBinURLPrefix + url, logger: logger) - .wait() - }.status - ) - - // No background activity expected here. - XCTAssertEqual(0, self.backgroundLogStore.allEntries.filter { $0.level >= .debug }.count) - - XCTAssertNoThrow( - try TemporaryFileHelpers.withTemporaryUnixDomainSocketPathName { path in - let backgroundLogStore = CollectEverythingLogHandler.LogStore() - var backgroundLogger = Logger( - label: "\(#function)", - factory: { _ in - CollectEverythingLogHandler(logStore: backgroundLogStore) - } - ) - backgroundLogger.logLevel = .trace - - let localSocketPathHTTPBin = HTTPBin(bindTarget: .unixDomainSocket(path)) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - backgroundActivityLogger: backgroundLogger - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localSocketPathHTTPBin.shutdown()) - } - - XCTAssertEqual( - .notFound, - try checkExpectationsWithLogger(type: "GET") { logger, url in - try localClient.execute(socketPath: path, urlPath: url, logger: logger).wait() - }.status - ) - - // No background activity expected here. - XCTAssertEqual(0, backgroundLogStore.allEntries.filter { $0.level >= .debug }.count) - } - ) - - XCTAssertNoThrow( - try TemporaryFileHelpers.withTemporaryUnixDomainSocketPathName { path in - let backgroundLogStore = CollectEverythingLogHandler.LogStore() - var backgroundLogger = Logger( - label: "\(#function)", - factory: { _ in - CollectEverythingLogHandler(logStore: backgroundLogStore) - } - ) - backgroundLogger.logLevel = .trace - - let localSocketPathHTTPBin = HTTPBin(.http1_1(ssl: true), bindTarget: .unixDomainSocket(path)) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: HTTPClient.Configuration(certificateVerification: .none), - backgroundActivityLogger: backgroundLogger - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localSocketPathHTTPBin.shutdown()) - } - - XCTAssertEqual( - .notFound, - try checkExpectationsWithLogger(type: "GET") { logger, url in - try localClient.execute(secureSocketPath: path, urlPath: url, logger: logger).wait() - }.status - ) - - // No background activity expected here. - XCTAssertEqual(0, backgroundLogStore.allEntries.filter { $0.level >= .debug }.count) - } - ) - } - - func testClosingIdleConnectionsInPoolLogsInTheBackground() { - XCTAssertNoThrow(try self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "/get").wait()) - - XCTAssertNoThrow(try self.defaultClient.syncShutdown()) - - XCTAssertGreaterThanOrEqual(self.backgroundLogStore.allEntries.count, 0) - XCTAssert( - self.backgroundLogStore.allEntries.contains { entry in - entry.message == "Shutting down connection pool" - } - ) - XCTAssert( - self.backgroundLogStore.allEntries.allSatisfy { entry in - entry.metadata["ahc-request-id"] == nil && entry.metadata["ahc-request"] == nil - && entry.metadata["ahc-pool-key"] != nil - } - ) - - self.defaultClient = nil // so it doesn't get shut down again. - } - - func testUploadStreamingNoLength() throws { - let server = NIOHTTP1TestServer(group: self.serverGroup) - let client = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup)) - defer { - XCTAssertNoThrow(try client.syncShutdown()) - XCTAssertNoThrow(try server.stop()) - } - - var request = try HTTPClient.Request(url: "http://localhost/(server.serverPort)/") - request.body = .stream { writer in - writer.write(.byteBuffer(ByteBuffer(string: "1234"))) - } - - let future = client.execute(request: request) - - switch try server.readInbound() { - case .head(let head): - XCTAssertEqual(head.headers["transfer-encoding"], ["chunked"]) - default: - XCTFail("Unexpected part") - } - - XCTAssertNoThrow(try server.readInbound()) // .body - XCTAssertNoThrow(try server.readInbound()) // .end - - XCTAssertNoThrow(try server.writeOutbound(.head(.init(version: .init(major: 1, minor: 1), status: .ok)))) - XCTAssertNoThrow(try server.writeOutbound(.end(nil))) - - XCTAssertNoThrow(try future.wait()) - } - - func testConnectErrorPropagatedToDelegate() throws { - final class TestDelegate: HTTPClientResponseDelegate { - typealias Response = Void - let error = NIOLockedValueBox(nil) - func didFinishRequest(task: HTTPClient.Task) throws {} - func didReceiveError(task: HTTPClient.Task, _ error: Error) { - self.error.withLockedValue { $0 = error } - } - } - - let httpClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: .init(timeout: .init(connect: .milliseconds(10))) - ) - - defer { - XCTAssertNoThrow(try httpClient.syncShutdown()) - } - - // This must throw as 198.51.100.254 is reserved for documentation only - let request = try HTTPClient.Request(url: "http://198.51.100.254:65535/get") - let delegate = TestDelegate() - - XCTAssertThrowsError(try httpClient.execute(request: request, delegate: delegate).wait()) { - XCTAssertEqualTypeAndValue($0, HTTPClientError.connectTimeout) - XCTAssertEqualTypeAndValue(delegate.error.withLockedValue { $0 }, HTTPClientError.connectTimeout) - } - } - - func testDelegateCallinsTolerateRandomEL() throws { - final class TestDelegate: HTTPClientResponseDelegate { - typealias Response = Void - let eventLoop: EventLoop - - init(eventLoop: EventLoop) { - self.eventLoop = eventLoop - } - - func didReceiveHead(task: HTTPClient.Task, _: HTTPResponseHead) -> EventLoopFuture { - self.eventLoop.makeSucceededFuture(()) - } - - func didReceiveBodyPart(task: HTTPClient.Task, _: ByteBuffer) -> EventLoopFuture { - self.eventLoop.makeSucceededFuture(()) - } - - func didFinishRequest(task: HTTPClient.Task) throws {} - } - - let elg = getDefaultEventLoopGroup(numberOfThreads: 3) - let first = elg.next() - let second = elg.next() - XCTAssertFalse(first === second) - - let httpServer = NIOHTTP1TestServer(group: self.serverGroup) - let httpClient = HTTPClient(eventLoopGroupProvider: .shared(first)) - defer { - XCTAssertNoThrow(try httpClient.syncShutdown()) - XCTAssertNoThrow(try httpServer.stop()) - XCTAssertNoThrow(try elg.syncShutdownGracefully()) - } - - let delegate = TestDelegate(eventLoop: second) - let request = try HTTPClient.Request(url: "http://localhost/(httpServer.serverPort)/") - let future = httpClient.execute(request: request, delegate: delegate) - - XCTAssertNoThrow(try httpServer.readInbound()) // .head - XCTAssertNoThrow(try httpServer.readInbound()) // .end - - XCTAssertNoThrow(try httpServer.writeOutbound(.head(.init(version: .init(major: 1, minor: 1), status: .ok)))) - XCTAssertNoThrow(try httpServer.writeOutbound(.body(.byteBuffer(ByteBuffer(string: "1234"))))) - XCTAssertNoThrow(try httpServer.writeOutbound(.end(nil))) - - XCTAssertNoThrow(try future.wait()) - } - - func testDelegateGetsErrorsFromCreatingRequestBag() throws { - // We want to test that we propagate errors to the delegate from failures to construct the - // request bag. Those errors only come from invalid headers. - final class TestDelegate: HTTPClientResponseDelegate, Sendable { - typealias Response = Void - let error: NIOLockedValueBox = .init(nil) - func didFinishRequest(task: HTTPClient.Task) throws {} - func didReceiveError(task: HTTPClient.Task, _ error: Error) { - self.error.withLockedValue { $0 = error } - } - } - - let httpClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup) - ) - - defer { - XCTAssertNoThrow(try httpClient.syncShutdown()) - } - - // 198.51.100.254 is reserved for documentation only - var request = try HTTPClient.Request(url: "http://198.51.100.254:65535/get") - request.headers.replaceOrAdd(name: "Not-ASCII", value: "not-fine\n") - let delegate = TestDelegate() - - XCTAssertThrowsError(try httpClient.execute(request: request, delegate: delegate).wait()) { - XCTAssertEqualTypeAndValue($0, HTTPClientError.invalidHeaderFieldValues(["not-fine\n"])) - XCTAssertEqualTypeAndValue( - delegate.error.withLockedValue { $0 }, - HTTPClientError.invalidHeaderFieldValues(["not-fine\n"]) - ) - } - } - - func testContentLengthTooLongFails() throws { - let url = self.defaultHTTPBinURLPrefix + "post" - let defaultClient = self.defaultClient! - XCTAssertThrowsError( - try self.defaultClient.execute( - request: - Request( - url: url, - body: .stream(contentLength: 10) { streamWriter in - let promise = defaultClient.eventLoopGroup.next().makePromise(of: Void.self) - DispatchQueue(label: "content-length-test").async { - streamWriter.write(.byteBuffer(ByteBuffer(string: "1"))).cascade(to: promise) - } - return promise.futureResult - } - ) - ).wait() - ) { error in - XCTAssertEqual(error as! HTTPClientError, HTTPClientError.bodyLengthMismatch) - } - // Quickly try another request and check that it works. - let response = try self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "get").wait() - guard var body = response.body else { - XCTFail("Body missing: \(response)") - return - } - guard let info = try body.readJSONDecodable(RequestInfo.self, length: body.readableBytes) else { - XCTFail("Cannot parse body: \(body.readableBytesView.map { $0 })") - return - } - XCTAssertEqual(info.connectionNumber, 1) - XCTAssertEqual(info.requestNumber, 1) - } - - // currently gets stuck because of #250 the server just never replies - func testContentLengthTooShortFails() throws { - let url = self.defaultHTTPBinURLPrefix + "post" - let tooLong = "XBAD BAD BAD NOT HTTP/1.1\r\n\r\n" - XCTAssertThrowsError( - try self.defaultClient.execute( - request: - Request( - url: url, - body: .stream(contentLength: 1) { streamWriter in - streamWriter.write(.byteBuffer(ByteBuffer(string: tooLong))) - } - ) - ).wait() - ) { error in - XCTAssertEqual(error as! HTTPClientError, HTTPClientError.bodyLengthMismatch) - } - // Quickly try another request and check that it works. If we by accident wrote some extra bytes into the - // stream (and reuse the connection) that could cause problems. - let response = try self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "get").wait() - guard var body = response.body else { - XCTFail("Body missing: \(response)") - return - } - guard let info = try body.readJSONDecodable(RequestInfo.self, length: body.readableBytes) else { - XCTFail("Cannot parse body: \(body.readableBytesView.map { $0 })") - return - } - XCTAssertEqual(info.connectionNumber, 1) - XCTAssertEqual(info.requestNumber, 1) - } - - func testBodyUploadAfterEndFails() { - let url = self.defaultHTTPBinURLPrefix + "post" - - let uploader = { @Sendable (_ streamWriter: HTTPClient.Body.StreamWriter) -> EventLoopFuture in - let done = streamWriter.write(.byteBuffer(ByteBuffer(string: "X"))) - done.recover { error in - XCTFail("unexpected error \(error)") - }.whenSuccess { - // This is executed when we have already sent the end of the request. - done.eventLoop.execute { - streamWriter.write(.byteBuffer(ByteBuffer(string: "BAD BAD BAD"))).whenComplete { result in - switch result { - case .success: - XCTFail("we succeeded writing bytes after the end!?") - case .failure(let error): - XCTAssertEqual(HTTPClientError.writeAfterRequestSent, error as? HTTPClientError) - } - } - } - } - return done - } - - var request: HTTPClient.Request? - XCTAssertNoThrow(request = try Request(url: url, body: .stream(contentLength: 1, uploader))) - XCTAssertThrowsError(try self.defaultClient.execute(request: XCTUnwrap(request)).wait()) { - XCTAssertEqual($0 as? HTTPClientError, .writeAfterRequestSent) - } - - // Quickly try another request and check that it works. If we by accident wrote some extra bytes into the - // stream (and reuse the connection) that could cause problems. - XCTAssertNoThrow(try self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "get").wait()) - } - - func testDoubleError() throws { - // This is needed to that connection pool will not get into closed state when we release - // second connection. - _ = self.defaultClient.get(url: "http://localhost/(self.defaultHTTPBin.port)/events/10/1") - - let clientGroup = self.clientGroup! - var request = try HTTPClient.Request(url: "http://localhost/(self.defaultHTTPBin.port)/wait", method: .POST) - request.body = .stream { writer in - // Start writing chunks so tha we will try to write after read timeout is thrown - for _ in 1...10 { - _ = writer.write(.byteBuffer(ByteBuffer(string: "1234"))) - } - - let promise = clientGroup.next().makePromise(of: Void.self) - clientGroup.next().scheduleTask(in: .milliseconds(3)) { - writer.write(.byteBuffer(ByteBuffer(string: "1234"))).cascade(to: promise) - } - - return promise.futureResult - } - - // We specify a deadline of 2 ms co that request will be timed out before all chunks are writtent, - // we need to verify that second error on write after timeout does not lead to double-release. - XCTAssertThrowsError( - try self.defaultClient.execute(request: request, deadline: .now() + .milliseconds(2)).wait() - ) - } - - func testSSLHandshakeErrorPropagation() throws { - final class CloseHandler: ChannelInboundHandler, Sendable { - typealias InboundIn = Any - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - context.close(promise: nil) - } - } - - let server = try ServerBootstrap(group: self.serverGroup) - .childChannelInitializer { channel in - channel.pipeline.addHandler(CloseHandler()) - } - .bind(host: "127.0.0.1", port: 0) - .wait() - - defer { - XCTAssertNoThrow(try server.close().wait()) - } - - var timeout = HTTPClient.Configuration.Timeout(connect: .seconds(10)) - if isTestingNIOTS() { - // If we are using Network.framework, we set the connect timeout down very low here - // because on NIOTS a failing TLS handshake manifests as a connect timeout. - // Note that we do this here to prove that we correctly manifest the underlying error: - // DO NOT CHANGE THIS TO DISABLE WAITING FOR CONNECTIVITY. - timeout.connect = .milliseconds(100) - } - - let config = HTTPClient.Configuration(timeout: timeout) - let client = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup), configuration: config) - defer { - XCTAssertNoThrow(try client.syncShutdown()) - } - - let request = try Request(url: "https://127.0.0.1/(server.localAddress!.port!)", method: .GET) - let task = client.execute(request: request, delegate: TestHTTPDelegate()) - - XCTAssertThrowsError(try task.wait()) { error in - if isTestingNIOTS() { - #if canImport(Network) - // We can't be more specific than this. - XCTAssertTrue(error is HTTPClient.NWTLSError) - #else - XCTFail("Impossible condition") - #endif - } else { - switch error as? NIOSSLError { - case .some(.handshakeFailed(.sslError(_))): break - default: XCTFail("Handshake failed with unexpected error: \(String(describing: error))") - } - } - } - } - - func testSSLHandshakeErrorPropagationDelayedClose() throws { - // This is as the test above, but the close handler delays its close action by a few hundred ms. - // This will tend to catch the pipeline at different weird stages, and flush out different bugs. - final class CloseHandler: ChannelInboundHandler, Sendable { - typealias InboundIn = Any - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - context.eventLoop.assumeIsolated().scheduleTask(in: .milliseconds(100)) { - context.close(promise: nil) - } - } - } - - let server = try ServerBootstrap(group: self.serverGroup) - .childChannelInitializer { channel in - channel.pipeline.addHandler(CloseHandler()) - } - .bind(host: "127.0.0.1", port: 0) - .wait() - - defer { - XCTAssertNoThrow(try server.close().wait()) - } - - var timeout = HTTPClient.Configuration.Timeout(connect: .seconds(10)) - if isTestingNIOTS() { - // If we are using Network.framework, we set the connect timeout down very low here - // because on NIOTS a failing TLS handshake manifests as a connect timeout. - timeout.connect = .milliseconds(300) - } - - let config = HTTPClient.Configuration(timeout: timeout) - let client = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup), configuration: config) - defer { - XCTAssertNoThrow(try client.syncShutdown()) - } - - let request = try Request(url: "https://127.0.0.1/(server.localAddress!.port!)", method: .GET) - let task = client.execute(request: request, delegate: TestHTTPDelegate()) - - XCTAssertThrowsError(try task.wait()) { error in - if isTestingNIOTS() { - #if canImport(Network) - // We can't be more specific than this. - XCTAssertTrue(error is HTTPClient.NWTLSError) - #else - XCTFail("Impossible condition") - #endif - } else { - switch error as? NIOSSLError { - case .some(.handshakeFailed(.sslError(_))): break - default: XCTFail("Handshake failed with unexpected error: \(String(describing: error))") - } - } - } - } - - func testWeCloseConnectionsWhenConnectionCloseSetByServer() throws { - let group = DispatchGroup() - group.enter() - - let server = try ServerBootstrap(group: self.serverGroup) - .serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1) - .childChannelInitializer { channel in - channel.pipeline.configureHTTPServerPipeline().flatMapThrowing { - try channel.pipeline.syncOperations.addHandler(CloseWithoutClosingServerHandler(group.leave)) - } - } - .bind(host: "localhost", port: 0) - .wait() - - defer { - server.close(promise: nil) - } - - // Simple request, should go great. - XCTAssertNoThrow(try self.defaultClient.get(url: "http://localhost/(server.localAddress!.port!)/").wait()) - - // Shouldn't need more than 100ms of waiting to see the close. - let result = group.wait(timeout: DispatchTime.now() + DispatchTimeInterval.milliseconds(100)) - XCTAssertEqual(result, .success, "we never closed the connection!") - } - - // In this test, we test that a request can continue to stream its body after the response head, - // was received. The client sends a number to the server and waits for the server to echo the - // number. Once the client receives the echoed number, it will continue with the next number. - // The client and server ping/pong 30 times. - func testBiDirectionalStreaming() { - let httpBin = HTTPBin(.http1_1(ssl: false, compress: false)) { _ in HTTPEchoHandler() } - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - let writeEL = eventLoopGroup.next() - let delegateEL = eventLoopGroup.next() - - let httpClient = HTTPClient(eventLoopGroupProvider: .shared(eventLoopGroup)) - defer { XCTAssertNoThrow(try httpClient.syncShutdown()) } - - let delegate = ResponseStreamDelegate(eventLoop: delegateEL) - - let body: HTTPClient.Body = .stream { writer in - let finalPromise = writeEL.makePromise(of: Void.self) - - @Sendable func writeLoop(_ writer: HTTPClient.Body.StreamWriter, index: Int) { - // always invoke from the wrong el to test thread safety - writeEL.preconditionInEventLoop() - - if index >= 30 { - return finalPromise.succeed(()) - } - - let sent = ByteBuffer(integer: index) - writer.write(.byteBuffer(sent)).flatMap { () -> EventLoopFuture in - // ensure, that the writer dispatches back to the expected delegate el. - delegateEL.preconditionInEventLoop() - return delegate.next() - }.whenComplete { result in - switch result { - case .success(let returned): - XCTAssertEqual(returned, sent) - - writeEL.execute { - writeLoop(writer, index: index + 1) - } - - case .failure(let error): - finalPromise.fail(error) - } - } - } - - writeEL.execute { - writeLoop(writer, index: 0) - } - - return finalPromise.futureResult - } - - let request = try! HTTPClient.Request(url: "http://localhost/(httpBin.port)", body: body) - let future = httpClient.execute(request: request, delegate: delegate, eventLoop: .delegate(on: delegateEL)) - - XCTAssertNoThrow(try future.wait()) - XCTAssertNil(try delegate.next().wait()) - } - - func testResponseAccumulatorMaxBodySizeLimitExceedingWithContentLength() throws { - let httpBin = HTTPBin(.http1_1(ssl: false, compress: false)) { _ in HTTPEchoHandler() } - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let body = ByteBuffer(bytes: 0..<11) - - var request = try Request(url: httpBin.baseURL) - request.body = .byteBuffer(body) - XCTAssertThrowsError( - try self.defaultClient.execute( - request: request, - delegate: ResponseAccumulator(request: request, maxBodySize: 10) - ).wait() - ) { error in - XCTAssertTrue(error is ResponseAccumulator.ResponseTooBigError, "unexpected error \(error)") - } - } - - func testResponseAccumulatorMaxBodySizeLimitNotExceedingWithContentLength() throws { - let httpBin = HTTPBin(.http1_1(ssl: false, compress: false)) { _ in HTTPEchoHandler() } - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let body = ByteBuffer(bytes: 0..<10) - - var request = try Request(url: httpBin.baseURL) - request.body = .byteBuffer(body) - let response = try self.defaultClient.execute( - request: request, - delegate: ResponseAccumulator(request: request, maxBodySize: 10) - ).wait() - - XCTAssertEqual(response.body, body) - } - - func testResponseAccumulatorMaxBodySizeLimitExceedingWithContentLengthButMethodIsHead() throws { - let httpBin = HTTPBin(.http1_1(ssl: false, compress: false)) { _ in HTTPEchoHeaders() } - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let body = ByteBuffer(bytes: 0..<11) - - var request = try Request(url: httpBin.baseURL, method: .HEAD) - request.body = .byteBuffer(body) - let response = try self.defaultClient.execute( - request: request, - delegate: ResponseAccumulator(request: request, maxBodySize: 10) - ).wait() - - XCTAssertEqual(response.body ?? ByteBuffer(), ByteBuffer()) - } - - func testResponseAccumulatorMaxBodySizeLimitExceedingWithTransferEncodingChuncked() throws { - let httpBin = HTTPBin(.http1_1(ssl: false, compress: false)) { _ in HTTPEchoHandler() } - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let body = ByteBuffer(bytes: 0..<11) - - var request = try Request(url: httpBin.baseURL) - request.body = .stream { writer in - writer.write(.byteBuffer(body)) - } - XCTAssertThrowsError( - try self.defaultClient.execute( - request: request, - delegate: ResponseAccumulator(request: request, maxBodySize: 10) - ).wait() - ) { error in - XCTAssertTrue(error is ResponseAccumulator.ResponseTooBigError, "unexpected error \(error)") - } - } - - func testResponseAccumulatorMaxBodySizeLimitNotExceedingWithTransferEncodingChuncked() throws { - let httpBin = HTTPBin(.http1_1(ssl: false, compress: false)) { _ in HTTPEchoHandler() } - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let body = ByteBuffer(bytes: 0..<10) - - var request = try Request(url: httpBin.baseURL) - request.body = .stream { writer in - writer.write(.byteBuffer(body)) - } - let response = try self.defaultClient.execute( - request: request, - delegate: ResponseAccumulator(request: request, maxBodySize: 10) - ).wait() - - XCTAssertEqual(response.body, body) - } - - // In this test, we test that a request can continue to stream its body after the response head and end - // was received where the end is a 200. - func testBiDirectionalStreamingEarly200() { - let httpBin = HTTPBin(.http1_1(ssl: false, compress: false)) { _ in - HTTP200DelayedHandler(bodyPartsBeforeResponse: 1) - } - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - let writeEL = eventLoopGroup.next() - let delegateEL = eventLoopGroup.next() - - let httpClient = HTTPClient(eventLoopGroupProvider: .shared(eventLoopGroup)) - defer { XCTAssertNoThrow(try httpClient.syncShutdown()) } - - let delegate = ResponseStreamDelegate(eventLoop: delegateEL) - - let body: HTTPClient.Body = .stream { writer in - let finalPromise = writeEL.makePromise(of: Void.self) - - @Sendable func writeLoop(_ writer: HTTPClient.Body.StreamWriter, index: Int) { - // always invoke from the wrong el to test thread safety - writeEL.preconditionInEventLoop() - - if index >= 30 { - return finalPromise.succeed(()) - } - - let sent = ByteBuffer(integer: index) - writer.write(.byteBuffer(sent)).whenComplete { result in - switch result { - case .success: - writeEL.execute { - writeLoop(writer, index: index + 1) - } - - case .failure(let error): - finalPromise.fail(error) - } - } - } - - writeEL.execute { - writeLoop(writer, index: 0) - } - - return finalPromise.futureResult - } - - let request = try! HTTPClient.Request(url: "http://localhost/(httpBin.port)", body: body) - let future = httpClient.execute(request: request, delegate: delegate, eventLoop: .delegate(on: delegateEL)) - XCTAssertNoThrow(try future.wait()) - XCTAssertNil(try delegate.next().wait()) - } - - // This test is identical to the one above, except that we send another request immediately after. This is a regression - // test for https://github.com/swift-server/async-http-client/issues/595. - func testBiDirectionalStreamingEarly200DoesntPreventUsFromSendingMoreRequests() { - let httpBin = HTTPBin(.http1_1(ssl: false, compress: false)) { _ in - HTTP200DelayedHandler(bodyPartsBeforeResponse: 1) - } - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - let writeEL = eventLoopGroup.next() - - let httpClient = HTTPClient(eventLoopGroupProvider: .shared(eventLoopGroup)) - defer { XCTAssertNoThrow(try httpClient.syncShutdown()) } - - let body: HTTPClient.Body = .stream { writer in - let finalPromise = writeEL.makePromise(of: Void.self) - - @Sendable func writeLoop(_ writer: HTTPClient.Body.StreamWriter, index: Int) { - // always invoke from the wrong el to test thread safety - writeEL.preconditionInEventLoop() - - if index >= 30 { - return finalPromise.succeed(()) - } - - let sent = ByteBuffer(integer: index) - writer.write(.byteBuffer(sent)).whenComplete { result in - switch result { - case .success: - writeEL.execute { - writeLoop(writer, index: index + 1) - } - - case .failure(let error): - finalPromise.fail(error) - } - } - } - - writeEL.execute { - writeLoop(writer, index: 0) - } - - return finalPromise.futureResult - } - - let request = try! HTTPClient.Request(url: "http://localhost/(httpBin.port)", body: body) - let future = httpClient.execute(request: request) - XCTAssertNoThrow(try future.wait()) - - // Try another request - let future2 = httpClient.execute(request: request) - XCTAssertNoThrow(try future2.wait()) - } - - // This test validates that we correctly close the connection after our body completes when we've streamed a - // body and received the 2XX response _before_ we finished our stream. - func testCloseConnectionAfterEarly2XXWhenStreaming() { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let onClosePromise = eventLoopGroup.next().makePromise(of: Void.self) - let httpBin = HTTPBin(.http1_1(ssl: false, compress: false)) { _ in - ExpectClosureServerHandler(onClosePromise: onClosePromise) - } - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let writeEL = eventLoopGroup.next() - - let httpClient = HTTPClient(eventLoopGroupProvider: .shared(eventLoopGroup)) - defer { XCTAssertNoThrow(try httpClient.syncShutdown()) } - - let body: HTTPClient.Body = .stream { writer in - let finalPromise = writeEL.makePromise(of: Void.self) - - @Sendable func writeLoop(_ writer: HTTPClient.Body.StreamWriter, index: Int) { - // always invoke from the wrong el to test thread safety - writeEL.preconditionInEventLoop() - - if index >= 30 { - return finalPromise.succeed(()) - } - - let sent = ByteBuffer(integer: index) - writer.write(.byteBuffer(sent)).whenComplete { result in - switch result { - case .success: - writeEL.execute { - writeLoop(writer, index: index + 1) - } - - case .failure(let error): - finalPromise.fail(error) - } - } - } - - writeEL.execute { - writeLoop(writer, index: 0) - } - - return finalPromise.futureResult - } - - let headers = HTTPHeaders([("Connection", "close")]) - let request = try! HTTPClient.Request(url: "http://localhost/(httpBin.port)", headers: headers, body: body) - let future = httpClient.execute(request: request) - XCTAssertNoThrow(try future.wait()) - XCTAssertNoThrow(try onClosePromise.futureResult.wait()) - } - - func testSynchronousHandshakeErrorReporting() throws { - // This only affects cases where we use NIOSSL. - guard !isTestingNIOTS() else { return } - - // We use a specially crafted client that has no cipher suites to offer. To do this we ask - // only for cipher suites incompatible with our TLS version. - var tlsConfig = TLSConfiguration.makeClientConfiguration() - tlsConfig.minimumTLSVersion = .tlsv13 - tlsConfig.maximumTLSVersion = .tlsv12 - tlsConfig.certificateVerification = .none - let localHTTPBin = HTTPBin(.http1_1(ssl: true)) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: HTTPClient.Configuration(tlsConfiguration: tlsConfig) - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - - XCTAssertThrowsError(try localClient.get(url: "https://localhost/(localHTTPBin.port)/").wait()) { error in - guard let clientError = error as? NIOSSLError, case NIOSSLError.handshakeFailed = clientError else { - XCTFail("Unexpected error: \(error)") - return - } - } - } - - func testFileDownloadChunked() throws { - var request = try Request(url: self.defaultHTTPBinURLPrefix + "chunked") - request.headers.add(name: "Accept", value: "text/event-stream") - - let response = - try TemporaryFileHelpers.withTemporaryFilePath { path -> FileDownloadDelegate.Response in - let delegate = try FileDownloadDelegate(path: path) - - let response = try self.defaultClient.execute( - request: request, - delegate: delegate - ) - .wait() - - try XCTAssertEqual(50, TemporaryFileHelpers.fileSize(path: path)) - - return response - } - - XCTAssertEqual(.ok, response.head.status) - XCTAssertEqual("chunked", response.head.headers.first(name: "transfer-encoding")) - XCTAssertFalse(response.head.headers.contains(name: "content-length")) - - XCTAssertEqual(nil, response.totalBytes) - XCTAssertEqual(50, response.receivedBytes) - } - - func testCloseWhileBackpressureIsExertedIsFine() throws { - let request = try Request(url: self.defaultHTTPBinURLPrefix + "close-on-response") - let delegate = DelayOnHeadDelegate(eventLoop: self.clientGroup.next()) { _, promise in - promise.futureResult.eventLoop.scheduleTask(in: .milliseconds(50)) { - promise.succeed(()) - } - } - - let resultFuture = self.defaultClient.execute(request: request, delegate: delegate) - - // The full response must be correctly delivered. - var data = try resultFuture.wait() - guard let info = try data.readJSONDecodable(RequestInfo.self, length: data.readableBytes) else { - XCTFail("Could not parse response") - return - } - XCTAssertEqual(info.data, "some body content") - } - - func testErrorAfterCloseWhileBackpressureExerted() throws { - enum ExpectedError: Error { - case expected - } - - let request = try Request(url: self.defaultHTTPBinURLPrefix + "close-on-response") - let delegate = DelayOnHeadDelegate(eventLoop: self.clientGroup.next()) { _, backpressurePromise in - backpressurePromise.fail(ExpectedError.expected) - } - - let resultFuture = self.defaultClient.execute(request: request, delegate: delegate) - - // The task must be failed. - XCTAssertThrowsError(try resultFuture.wait()) { error in - XCTAssertEqual(error as? ExpectedError, .expected) - } - } - - func testRequestSpecificTLS() throws { - let configuration = HTTPClient.Configuration( - tlsConfiguration: nil, - timeout: .init(), - decompression: .disabled - ) - let localHTTPBin = HTTPBin(.http1_1(ssl: true)) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: configuration - ) - let decoder = JSONDecoder() - - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - - // First two requests use identical TLS configurations. - var tlsConfig = TLSConfiguration.makeClientConfiguration() - tlsConfig.certificateVerification = .none - let firstRequest = try HTTPClient.Request( - url: "https://localhost/(localHTTPBin.port)/get", - method: .GET, - tlsConfiguration: tlsConfig - ) - let firstResponse = try localClient.execute(request: firstRequest).wait() - guard let firstBody = firstResponse.body else { - XCTFail("No request body found") - return - } - let firstConnectionNumber = try decoder.decode(RequestInfo.self, from: firstBody).connectionNumber - - let secondRequest = try HTTPClient.Request( - url: "https://localhost/(localHTTPBin.port)/get", - method: .GET, - tlsConfiguration: tlsConfig - ) - let secondResponse = try localClient.execute(request: secondRequest).wait() - guard let secondBody = secondResponse.body else { - XCTFail("No request body found") - return - } - let secondConnectionNumber = try decoder.decode(RequestInfo.self, from: secondBody).connectionNumber - - // Uses a differrent TLS config. - var tlsConfig2 = TLSConfiguration.makeClientConfiguration() - tlsConfig2.certificateVerification = .none - tlsConfig2.maximumTLSVersion = .tlsv1 - let thirdRequest = try HTTPClient.Request( - url: "https://localhost/(localHTTPBin.port)/get", - method: .GET, - tlsConfiguration: tlsConfig2 - ) - let thirdResponse = try localClient.execute(request: thirdRequest).wait() - guard let thirdBody = thirdResponse.body else { - XCTFail("No request body found") - return - } - let thirdConnectionNumber = try decoder.decode(RequestInfo.self, from: thirdBody).connectionNumber - - XCTAssertEqual(firstResponse.status, .ok) - XCTAssertEqual(secondResponse.status, .ok) - XCTAssertEqual(thirdResponse.status, .ok) - XCTAssertEqual( - firstConnectionNumber, - secondConnectionNumber, - "Identical TLS configurations did not use the same connection" - ) - XCTAssertNotEqual( - thirdConnectionNumber, - firstConnectionNumber, - "Different TLS configurations did not use different connections." - ) - } - - func testRequestWithHeaderTransferEncodingIdentityDoesNotFail() { - let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try group.syncShutdownGracefully()) } - - let client = HTTPClient(eventLoopGroupProvider: .shared(group)) - defer { XCTAssertNoThrow(try client.syncShutdown()) } - - let httpBin = HTTPBin() - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - guard var request = try? Request(url: "http://127.0.0.1/(httpBin.port)/get") else { - return XCTFail("Expected to have a request here.") - } - request.headers.add(name: "X-Test-Header", value: "X-Test-Value") - request.headers.add(name: "Transfer-Encoding", value: "identity") - request.body = .string("1234") - - XCTAssertNoThrow(try client.execute(request: request).wait()) - } - - func testMassiveDownload() { - var response: HTTPClient.Response? - XCTAssertNoThrow( - response = try self.defaultClient.get(url: "\(self.defaultHTTPBinURLPrefix)mega-chunked").wait() - ) - - XCTAssertEqual(.ok, response?.status) - XCTAssertEqual(response?.version, .http1_1) - XCTAssertEqual(response?.body?.readableBytes, 10_000) - } - - func testShutdownWithFutures() { - let httpClient = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup)) - XCTAssertNoThrow(try httpClient.shutdown().wait()) - } - - func testMassiveHeaderHTTP1() throws { - var request = try HTTPClient.Request(url: defaultHTTPBin.baseURL, method: .POST) - // add ~64 KB header - let headerValue = String(repeating: "0", count: 1024) - for headerID in 0..<64 { - request.headers.replaceOrAdd(name: "larg-header-\(headerID)", value: headerValue) - } - - // non empty body is important to trigger this bug as we otherwise finish the request in a single flush - request.body = .byteBuffer(ByteBuffer(bytes: [0])) - - XCTAssertNoThrow(try defaultClient.execute(request: request).wait()) - } - - func testMassiveHeaderHTTP2() throws { - let bin = HTTPBin( - .http2(settings: [ - .init(parameter: .maxConcurrentStreams, value: 100), - .init(parameter: .maxHeaderListSize, value: 1024 * 256), - .init(parameter: .maxFrameSize, value: 1024 * 256), - ]) - ) - defer { XCTAssertNoThrow(try bin.shutdown()) } - - let client = HTTPClient( - eventLoopGroupProvider: .shared(clientGroup), - configuration: .init(certificateVerification: .none) - ) - - defer { XCTAssertNoThrow(try client.syncShutdown()) } - - var request = try HTTPClient.Request(url: bin.baseURL, method: .POST) - // add ~200 KB header - let headerValue = String(repeating: "0", count: 1024) - for headerID in 0..<200 { - request.headers.replaceOrAdd(name: "larg-header-\(headerID)", value: headerValue) - } - - // non empty body is important to trigger this bug as we otherwise finish the request in a single flush - request.body = .byteBuffer(ByteBuffer(bytes: [0])) - - XCTAssertNoThrow(try client.execute(request: request).wait()) - } - - func testCancelingRequestAfterRedirect() throws { - let request = try Request( - url: self.defaultHTTPBinURLPrefix + "redirect/target", - method: .GET, - headers: ["X-Target-Redirect-URL": self.defaultHTTPBinURLPrefix + "wait"], - body: nil - ) - - final class CancelAfterRedirect: HTTPClientResponseDelegate, Sendable { - init() {} - func didFinishRequest(task: AsyncHTTPClient.HTTPClient.Task) throws {} - } - - let task = defaultClient.execute( - request: request, - delegate: CancelAfterRedirect(), - deadline: .now() + .seconds(1) - ) - - // there is currently no HTTPClientResponseDelegate method to ensure the redirect occurs before we cancel, so we just sleep for 500ms - Thread.sleep(forTimeInterval: 0.5) - - task.cancel() - - XCTAssertThrowsError(try task.wait()) { error in - guard case let error = error as? HTTPClientError, error == .cancelled else { - return XCTFail("Should fail with cancelled") - } - } - } - - func testFailingRequestAfterRedirect() throws { - let request = try Request( - url: self.defaultHTTPBinURLPrefix + "redirect/target", - method: .GET, - headers: ["X-Target-Redirect-URL": self.defaultHTTPBinURLPrefix + "wait"], - body: nil - ) - - final class FailAfterRedirect: HTTPClientResponseDelegate, Sendable { - init() {} - func didFinishRequest(task: AsyncHTTPClient.HTTPClient.Task) throws {} - } - - let task = defaultClient.execute( - request: request, - delegate: FailAfterRedirect(), - deadline: .now() + .seconds(1) - ) - - // there is currently no HTTPClientResponseDelegate method to ensure the redirect occurs before we fail, so we just sleep for 500ms - Thread.sleep(forTimeInterval: 0.5) - - struct TestError: Error {} - - task.fail(reason: TestError()) - - XCTAssertThrowsError(try task.wait()) { error in - guard error is TestError else { - return XCTFail("Should fail with TestError") - } - } - } - - func testCancelingHTTP1RequestAfterHeaderSend() throws { - var request = try HTTPClient.Request(url: self.defaultHTTPBin.baseURL + "/wait", method: .POST) - // non-empty body is important - request.body = .byteBuffer(ByteBuffer([1])) - - final class CancelAfterHeadSend: HTTPClientResponseDelegate, Sendable { - init() {} - func didFinishRequest(task: AsyncHTTPClient.HTTPClient.Task) throws {} - func didSendRequestHead(task: HTTPClient.Task, _ head: HTTPRequestHead) { - task.cancel() - } - } - XCTAssertThrowsError(try defaultClient.execute(request: request, delegate: CancelAfterHeadSend()).wait()) - } - - func testCancelingHTTP2RequestAfterHeaderSend() throws { - let bin = HTTPBin(.http2()) - defer { XCTAssertNoThrow(try bin.shutdown()) } - var request = try HTTPClient.Request(url: bin.baseURL + "/wait", method: .POST) - // non-empty body is important - request.body = .byteBuffer(ByteBuffer([1])) - - final class CancelAfterHeadSend: HTTPClientResponseDelegate, Sendable { - init() {} - func didFinishRequest(task: AsyncHTTPClient.HTTPClient.Task) throws {} - func didSendRequestHead(task: HTTPClient.Task, _ head: HTTPRequestHead) { - task.cancel() - } - } - XCTAssertThrowsError(try defaultClient.execute(request: request, delegate: CancelAfterHeadSend()).wait()) - } - - private func testMaxConnectionReuses(mode: HTTPBin.Mode, maximumUses: Int, requests: Int) throws { - let bin = HTTPBin(mode) - defer { XCTAssertNoThrow(try bin.shutdown()) } - - var configuration = HTTPClient.Configuration(certificateVerification: .none) - // Limit each connection to two uses before discarding them. The test will verify that the - // connection number indicated by the server increments every two requests. - configuration.maximumUsesPerConnection = maximumUses - - let client = HTTPClient(eventLoopGroupProvider: .shared(self.clientGroup), configuration: configuration) - defer { XCTAssertNoThrow(try client.syncShutdown()) } - - let request = try HTTPClient.Request(url: bin.baseURL + "stats") - let decoder = JSONDecoder() - - // Do two requests per batch. Both should report the same connection number. - for requestNumber in stride(from: 0, to: requests, by: maximumUses) { - var responses = [RequestInfo]() - - for _ in 0..(0) - var executionCount: Int { self._executionCount.withLockedValue { $0 } } - - /// The minimum time to spend running the debug initializer. - static let duration: TimeAmount = .milliseconds(300) - - /// The actual debug initializer. - func initialize(channel: Channel) -> EventLoopFuture { - self._executionCount.withLockedValue { $0 += 1 } - - let someScheduledTask = channel.eventLoop.scheduleTask(in: Self.duration) { - channel.eventLoop.makeSucceededVoidFuture() - } - - return someScheduledTask.futureResult.flatMap { $0 } - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTPClientUncleanSSLConnectionShutdownTests.swift b/Tests/AsyncHTTPClientTests/HTTPClientUncleanSSLConnectionShutdownTests.swift deleted file mode 100644 index b63eb7cba..000000000 --- a/Tests/AsyncHTTPClientTests/HTTPClientUncleanSSLConnectionShutdownTests.swift +++ /dev/null @@ -1,322 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -// These tests may require some more context: -// -// TLS (SSL) encrypts, validates, and authenticates all the data that goes through the connection. -// That is a fantastic property to have and solves most issues. TLS however still runs over TCP and -// the control packets of TCP are not encrypted. This means that the one thing an attacker can still -// do on a TLS connection is to close it. The attacker could send RST or FIN packets the other peer -// and the receiving peer has no means to verify if this RST/FIN packet is actually coming from the -// other peer (as opposed to an attacker). -// -// To fix this problem, TLS introduces a close_notify message that is send over connection as -// encrypted data. So if the other peer receives a close_notify it knows that it was truly sent by -// the other peer (and not an attacker). A well behaving peer would then reply to the close_notify -// with its own close_notify and after that both peers can close the TCP connection because they -// know that the respective other peer knows they're okay closing it. -// -// Okay, but what's the issue with having a connection just close. Wouldn't I notice that part of -// the data is missing? The answer is it depends. Many protocols actually send to the other peer how -// much data they will send before sending the data. And they also have a well defined -// "end message". If you're using such a protocol, then an "unclean shutdown" (which is you have -// received a RST/FIN without a close_notify) is totally harmless. The higher level protocol allows -// you to distinguish between a truncated message (when there's still outstanding data) and a close -// after a completed message. -// -// The reason SwiftNIO sends you a NIOSSLError.uncleanShutdown if it sees a connection closing -// without a prior close_notify is because it doesn't know what protocol you're implementing. Maybe -// the protocol you speak doesn't transmit length information so a truncated message cannot be told -// apart from a complete message. -// -// Let's go into some example protocols and their behaviour regarding framing: -// -// - With HTTP/2 the other peer always knows how much data to expect, so an unclean shutdown is -// totally harmless, the error can be ignored. -// - With HTTP/1, the situation is much more complicated: HTTP/1 when using the content-length -// header is unaffected by truncation attacks because we know the content length. So if the -// connection closes before we have received that many bytes, we know it was a truncated message -// (either by an attacker injecting a FIN/RST, or by a software/network problem somewhere along -// the way) -// - HTTP/1 with transfer-encoding: chunked is also unaffected by truncation attacks because each -// chunk sends its length and there's a special "END" chunk (0\r\n\r\n). Unfortunately HTTP/1 can -// be used without either transfer-encoding: chunked or content-length. It then runs in the "EOF -// framing" mode which means that the message ends when we receive a connection close 😢 . Very -// bad, if HTTP/1 is used in "EOF framing" mode, then an unclean shutdown is actually a real -// error because we cannot tell a truncated message apart from a complete message. -// -// From @weissi in https://github.com/swift-server/async-http-client/issues/238 - -import AsyncHTTPClient -import Logging -import NIOCore -import NIOHTTP1 -import NIOPosix -import NIOSSL -import XCTest - -final class HTTPClientUncleanSSLConnectionShutdownTests: XCTestCase { - func testEOFFramedSuccess() { - let httpBin = HTTPBinForSSLUncleanShutdown() - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let client = HTTPClient( - eventLoopGroupProvider: .shared(eventLoopGroup), - configuration: .init(certificateVerification: .none) - ) - defer { XCTAssertNoThrow(try client.syncShutdown()) } - - XCTAssertThrowsError(try client.get(url: "https://localhost/(httpBin.port)/nocontentlength").wait()) { - XCTAssertEqual($0 as? NIOSSLError, .uncleanShutdown) - } - } - - func testContentLength() { - let httpBin = HTTPBinForSSLUncleanShutdown() - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let client = HTTPClient( - eventLoopGroupProvider: .shared(eventLoopGroup), - configuration: .init(certificateVerification: .none) - ) - defer { XCTAssertNoThrow(try client.syncShutdown()) } - - var response: HTTPClient.Response? - XCTAssertNoThrow(response = try client.get(url: "https://localhost/(httpBin.port)/contentlength").wait()) - XCTAssertEqual(response?.status, .notFound) - XCTAssertEqual(response?.headers["content-length"].first, "9") - } - - func testContentLengthButTruncated() { - let httpBin = HTTPBinForSSLUncleanShutdown() - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let client = HTTPClient( - eventLoopGroupProvider: .shared(eventLoopGroup), - configuration: .init(certificateVerification: .none) - ) - defer { XCTAssertNoThrow(try client.syncShutdown()) } - - XCTAssertThrowsError(try client.get(url: "https://localhost/(httpBin.port)/wrongcontentlength").wait()) { - XCTAssertEqual($0 as? HTTPParserError, .invalidEOFState) - } - } - - func testTransferEncoding() { - let httpBin = HTTPBinForSSLUncleanShutdown() - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let client = HTTPClient( - eventLoopGroupProvider: .shared(eventLoopGroup), - configuration: .init(certificateVerification: .none) - ) - defer { XCTAssertNoThrow(try client.syncShutdown()) } - - var response: HTTPClient.Response? - XCTAssertNoThrow(response = try client.get(url: "https://localhost/(httpBin.port)/transferencoding").wait()) - XCTAssertEqual(response?.status, .ok) - XCTAssertEqual(response?.headers["transfer-encoding"].first, "chunked") - XCTAssertEqual(response?.body, ByteBuffer(string: "foo")) - } - - func testTransferEncodingButTruncated() { - let httpBin = HTTPBinForSSLUncleanShutdown() - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let client = HTTPClient( - eventLoopGroupProvider: .shared(eventLoopGroup), - configuration: .init(certificateVerification: .none) - ) - defer { XCTAssertNoThrow(try client.syncShutdown()) } - - XCTAssertThrowsError(try client.get(url: "https://localhost/(httpBin.port)/transferencodingtruncated").wait()) - { - XCTAssertEqual($0 as? HTTPParserError, .invalidEOFState) - } - } - - func testConnectionDrop() { - let httpBin = HTTPBinForSSLUncleanShutdown() - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let client = HTTPClient( - eventLoopGroupProvider: .shared(eventLoopGroup), - configuration: .init(certificateVerification: .none) - ) - defer { XCTAssertNoThrow(try client.syncShutdown()) } - - XCTAssertThrowsError(try client.get(url: "https://localhost/(httpBin.port)/noresponse").wait()) { - XCTAssertEqual($0 as? HTTPClientError, .remoteConnectionClosed) - } - } -} - -final class HTTPBinForSSLUncleanShutdown { - let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) - let serverChannel: Channel - - var port: Int { - Int(self.serverChannel.localAddress!.port!) - } - - init() { - let configuration = TLSConfiguration.makeServerConfiguration( - certificateChain: [.certificate(TestTLS.certificate)], - privateKey: .privateKey(TestTLS.privateKey) - ) - let context = try! NIOSSLContext(configuration: configuration) - - self.serverChannel = try! ServerBootstrap(group: self.group) - .serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) - .childChannelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1) - .childChannelInitializer { channel in - do { - let requestDecoder = HTTPRequestDecoder() - let sync = channel.pipeline.syncOperations - - try sync.addHandler(ConnectionForceCloser()) - try sync.addHandler(NIOSSLServerHandler(context: context)) - try sync.addHandler(ByteToMessageHandler(requestDecoder)) - try sync.addHandler(HTTPBinForSSLUncleanShutdownHandler()) - return channel.eventLoop.makeSucceededVoidFuture() - } catch { - return channel.eventLoop.makeFailedFuture(error) - } - }.bind(host: "127.0.0.1", port: 0).wait() - } - - func shutdown() throws { - try self.group.syncShutdownGracefully() - } -} - -private final class HTTPBinForSSLUncleanShutdownHandler: ChannelInboundHandler { - typealias InboundIn = HTTPServerRequestPart - typealias OutboundOut = ByteBuffer - - init() {} - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - switch self.unwrapInboundIn(data) { - case .head(let req): - let response: String? - switch req.uri { - case "/nocontentlength": - response = """ - HTTP/1.1 200 OK\r\n\ - Connection: close\r\n\ - \r\n\ - foo - """ - - case "/nocontent": - response = """ - HTTP/1.1 204 OK\r\n\ - Connection: close\r\n\ - \r\n - """ - - case "/noresponse": - response = nil - - case "/wrongcontentlength": - response = """ - HTTP/1.1 200 OK\r\n\ - Connection: close\r\n\ - Content-Length: 6\r\n\ - \r\n\ - foo - """ - - case "/transferencoding": - response = """ - HTTP/1.1 200 OK\r\n\ - Connection: close\r\n\ - Transfer-Encoding: chunked\r\n\ - \r\n\ - 3\r\n\ - foo\r\n\ - 0\r\n\ - \r\n - """ - - case "/transferencodingtruncated": - response = """ - HTTP/1.1 200 OK\r\n\ - Connection: close\r\n\ - Transfer-Encoding: chunked\r\n\ - \r\n\ - 12\r\n\ - foo - """ - - default: - response = """ - HTTP/1.1 404 OK\r\n\ - Connection: close\r\n\ - Content-Length: 9\r\n\ - \r\n\ - Not Found - """ - } - - if let response = response { - var buffer = context.channel.allocator.buffer(capacity: response.count) - buffer.writeString(response) - context.writeAndFlush(self.wrapOutboundOut(buffer), promise: nil) - } - - context.triggerUserOutboundEvent(ConnectionForceCloser.CloseEvent(), promise: nil) - case .body: - () - case .end: - () - } - } -} - -private final class ConnectionForceCloser: ChannelOutboundHandler { - typealias OutboundIn = NIOAny - - struct CloseEvent {} - - init() {} - - func triggerUserOutboundEvent(context: ChannelHandlerContext, event: Any, promise: EventLoopPromise?) { - switch event { - case is CloseEvent: - context.close(promise: promise) - default: - context.triggerUserOutboundEvent(event, promise: promise) - } - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTPConnectionPool+FactoryTests.swift b/Tests/AsyncHTTPClientTests/HTTPConnectionPool+FactoryTests.swift deleted file mode 100644 index 15cc9e7e9..000000000 --- a/Tests/AsyncHTTPClientTests/HTTPConnectionPool+FactoryTests.swift +++ /dev/null @@ -1,212 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOCore -import NIOPosix -import NIOSOCKS -import NIOSSL -import XCTest - -@testable import AsyncHTTPClient - -class HTTPConnectionPool_FactoryTests: XCTestCase { - func testConnectionCreationTimesoutIfDeadlineIsInThePast() { - let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try group.syncShutdownGracefully()) } - - var server: Channel? - XCTAssertNoThrow( - server = try ServerBootstrap(group: group) - .childChannelInitializer { channel in - channel.pipeline.addHandler(NeverrespondServerHandler()) - } - .bind(to: .init(ipAddress: "127.0.0.1", port: 0)) - .wait() - ) - defer { - XCTAssertNoThrow(try server?.close().wait()) - } - - let request = try! HTTPClient.Request(url: "https://apple.com/") - - let factory = HTTPConnectionPool.ConnectionFactory( - key: .init(request), - tlsConfiguration: nil, - clientConfiguration: .init(proxy: .socksServer(host: "127.0.0.1", port: server!.localAddress!.port!)), - sslContextCache: .init() - ) - - XCTAssertThrowsError( - try factory.makeChannel( - requester: ExplodingRequester(), - connectionID: 1, - deadline: .now() - .seconds(1), - eventLoop: group.next(), - logger: .init(label: "test") - ).wait() - ) { - XCTAssertEqual($0 as? HTTPClientError, .connectTimeout) - } - } - - func testSOCKSConnectionCreationTimesoutIfRemoteIsUnresponsive() { - let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try group.syncShutdownGracefully()) } - - var server: Channel? - XCTAssertNoThrow( - server = try ServerBootstrap(group: group) - .childChannelInitializer { channel in - channel.pipeline.addHandler(NeverrespondServerHandler()) - } - .bind(to: .init(ipAddress: "127.0.0.1", port: 0)) - .wait() - ) - defer { - XCTAssertNoThrow(try server?.close().wait()) - } - - let request = try! HTTPClient.Request(url: "https://apple.com/") - - let factory = HTTPConnectionPool.ConnectionFactory( - key: .init(request), - tlsConfiguration: nil, - clientConfiguration: .init(proxy: .socksServer(host: "127.0.0.1", port: server!.localAddress!.port!)) - .enableFastFailureModeForTesting(), - sslContextCache: .init() - ) - - XCTAssertThrowsError( - try factory.makeChannel( - requester: ExplodingRequester(), - connectionID: 1, - deadline: .now() + .seconds(1), - eventLoop: group.next(), - logger: .init(label: "test") - ).wait() - ) { - XCTAssertEqual($0 as? HTTPClientError, .socksHandshakeTimeout) - } - } - - func testHTTPProxyConnectionCreationTimesoutIfRemoteIsUnresponsive() { - let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try group.syncShutdownGracefully()) } - - var server: Channel? - XCTAssertNoThrow( - server = try ServerBootstrap(group: group) - .childChannelInitializer { channel in - channel.pipeline.addHandler(NeverrespondServerHandler()) - } - .bind(to: .init(ipAddress: "127.0.0.1", port: 0)) - .wait() - ) - defer { - XCTAssertNoThrow(try server?.close().wait()) - } - - let request = try! HTTPClient.Request(url: "https://localhost/(server!.localAddress!.port!)") - - let factory = HTTPConnectionPool.ConnectionFactory( - key: .init(request), - tlsConfiguration: nil, - clientConfiguration: .init(proxy: .server(host: "127.0.0.1", port: server!.localAddress!.port!)) - .enableFastFailureModeForTesting(), - sslContextCache: .init() - ) - - XCTAssertThrowsError( - try factory.makeChannel( - requester: ExplodingRequester(), - connectionID: 1, - deadline: .now() + .seconds(1), - eventLoop: group.next(), - logger: .init(label: "test") - ).wait() - ) { - XCTAssertEqual($0 as? HTTPClientError, .httpProxyHandshakeTimeout) - } - } - - func testTLSConnectionCreationTimesoutIfRemoteIsUnresponsive() { - let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try group.syncShutdownGracefully()) } - - var server: Channel? - XCTAssertNoThrow( - server = try ServerBootstrap(group: group) - .childChannelInitializer { channel in - channel.pipeline.addHandler(NeverrespondServerHandler()) - } - .bind(to: .init(ipAddress: "127.0.0.1", port: 0)) - .wait() - ) - defer { - XCTAssertNoThrow(try server?.close().wait()) - } - - let request = try! HTTPClient.Request(url: "https://localhost/(server!.localAddress!.port!)") - - var tlsConfig = TLSConfiguration.makeClientConfiguration() - tlsConfig.certificateVerification = .none - let factory = HTTPConnectionPool.ConnectionFactory( - key: .init(request), - tlsConfiguration: nil, - clientConfiguration: .init(tlsConfiguration: tlsConfig) - .enableFastFailureModeForTesting(), - sslContextCache: .init() - ) - - XCTAssertThrowsError( - try factory.makeChannel( - requester: ExplodingRequester(), - connectionID: 1, - deadline: .now() + .seconds(1), - eventLoop: group.next(), - logger: .init(label: "test") - ).wait() - ) { - XCTAssertEqual($0 as? HTTPClientError, .tlsHandshakeTimeout) - } - } -} - -final class NeverrespondServerHandler: ChannelInboundHandler, Sendable { - typealias InboundIn = NIOAny - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - // do nothing - } -} - -/// A `HTTPConnectionRequester` that will fail a test if any of its methods are ever called. -final class ExplodingRequester: HTTPConnectionRequester { - func http1ConnectionCreated(_: HTTP1Connection.SendableView) { - XCTFail("http1ConnectionCreated called unexpectedly") - } - - func http2ConnectionCreated(_: HTTP2Connection.SendableView, maximumStreams: Int) { - XCTFail("http2ConnectionCreated called unexpectedly") - } - - func failedToCreateHTTPConnection(_: HTTPConnectionPool.Connection.ID, error: Error) { - XCTFail("failedToCreateHTTPConnection called unexpectedly") - } - - func waitingForConnectivity(_: HTTPConnectionPool.Connection.ID, error: Error) { - XCTFail("waitingForConnectivity called unexpectedly") - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTPConnectionPool+HTTP1ConnectionsTest.swift b/Tests/AsyncHTTPClientTests/HTTPConnectionPool+HTTP1ConnectionsTest.swift deleted file mode 100644 index 914990048..000000000 --- a/Tests/AsyncHTTPClientTests/HTTPConnectionPool+HTTP1ConnectionsTest.swift +++ /dev/null @@ -1,708 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOEmbedded -import XCTest - -@testable import AsyncHTTPClient - -class HTTPConnectionPool_HTTP1ConnectionsTests: XCTestCase { - func testCreatingConnections() { - let elg = EmbeddedEventLoopGroup(loops: 4) - var connections = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 8, - generator: .init(), - maximumConnectionUses: nil - ) - - let el1 = elg.next() - let el2 = elg.next() - - // general purpose connection - XCTAssertEqual(connections.startingGeneralPurposeConnections, 0) - XCTAssertEqual(connections.startingEventLoopConnections(on: el1), 0) - let conn1ID = connections.createNewConnection(on: el1) - XCTAssertEqual(connections.startingGeneralPurposeConnections, 1) - XCTAssertEqual(connections.startingEventLoopConnections(on: el1), 0) - let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1) - let (conn1Index, conn1CreatedContext) = connections.newHTTP1ConnectionEstablished(conn1) - XCTAssertEqual(conn1CreatedContext.use, .generalPurpose) - XCTAssert(conn1CreatedContext.eventLoop === el1) - XCTAssertEqual(connections.leaseConnection(at: conn1Index), conn1) - XCTAssertEqual(connections.startingGeneralPurposeConnections, 0) - - // eventLoop connection - let conn2ID = connections.createNewOverflowConnection(on: el2) - XCTAssertEqual(connections.startingGeneralPurposeConnections, 0) - XCTAssertEqual(connections.startingEventLoopConnections(on: el2), 1) - let conn2: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn2ID, eventLoop: el2) - let (conn2Index, conn2CreatedContext) = connections.newHTTP1ConnectionEstablished(conn2) - XCTAssertEqual(conn2CreatedContext.use, .eventLoop(el2)) - XCTAssert(conn2CreatedContext.eventLoop === el2) - XCTAssertEqual(connections.leaseConnection(at: conn2Index), conn2) - XCTAssertEqual(connections.startingEventLoopConnections(on: el2), 0) - } - - func testCreatingConnectionAndFailing() { - let elg = EmbeddedEventLoopGroup(loops: 4) - var connections = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 8, - generator: .init(), - maximumConnectionUses: nil - ) - - let el1 = elg.next() - let el2 = elg.next() - - // general purpose connection - XCTAssertEqual(connections.startingGeneralPurposeConnections, 0) - XCTAssertEqual(connections.startingEventLoopConnections(on: el1), 0) - let conn1ID = connections.createNewConnection(on: el1) - XCTAssertEqual(conn1ID, 0) - XCTAssertEqual(connections.startingGeneralPurposeConnections, 1) - XCTAssertEqual(connections.startingEventLoopConnections(on: el1), 0) - // connection failed to start. 1. backoff - let backoff1EL = connections.backoffNextConnectionAttempt(conn1ID) - XCTAssert(backoff1EL === el1) - // backoff done. 2. decide what's next - guard let (conn1FailIndex, conn1FailContext) = connections.failConnection(conn1ID) else { - return XCTFail("Expected that the connection is remembered") - } - XCTAssert(conn1FailContext.eventLoop === el1) - XCTAssertEqual(conn1FailContext.use, .generalPurpose) - XCTAssertEqual(conn1FailContext.connectionsStartingForUseCase, 0) - let (replaceConn1ID, replaceConn1EL) = connections.replaceConnection(at: conn1FailIndex) - XCTAssert(replaceConn1EL === el1) - XCTAssertEqual(replaceConn1ID, 1) - - // eventLoop connection - let conn2ID = connections.createNewOverflowConnection(on: el2) - // the replacement connection is starting - XCTAssertEqual(connections.startingGeneralPurposeConnections, 1) - XCTAssertEqual(connections.startingEventLoopConnections(on: el2), 1) - let backoff2EL = connections.backoffNextConnectionAttempt(conn2ID) - XCTAssert(backoff2EL === el2) - guard let (conn2FailIndex, conn2FailContext) = connections.failConnection(conn2ID) else { - return XCTFail("Expected that the connection is remembered") - } - XCTAssert(conn2FailContext.eventLoop === el2) - XCTAssertEqual(conn2FailContext.use, .eventLoop(el2)) - XCTAssertEqual(conn2FailContext.connectionsStartingForUseCase, 0) - connections.removeConnection(at: conn2FailIndex) - // the replacement connection is still starting - XCTAssertEqual(connections.startingGeneralPurposeConnections, 1) - } - - func testLeaseConnectionOnPreferredAndAvailableEL() { - let elg = EmbeddedEventLoopGroup(loops: 4) - let el1 = elg.next() - let el2 = elg.next() - let el3 = elg.next() - let el4 = elg.next() - - var connections = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 8, - generator: .init(), - maximumConnectionUses: nil - ) - - for el in [el1, el2, el3, el4] { - XCTAssertEqual(connections.startingGeneralPurposeConnections, 0) - XCTAssertEqual(connections.startingEventLoopConnections(on: el), 0) - let connID = connections.createNewConnection(on: el) - XCTAssertEqual(connections.startingGeneralPurposeConnections, 1) - XCTAssertEqual(connections.startingEventLoopConnections(on: el), 0) - let conn: HTTPConnectionPool.Connection = .__testOnly_connection(id: connID, eventLoop: el) - let (_, connCreatedContext) = connections.newHTTP1ConnectionEstablished(conn) - XCTAssertEqual(connCreatedContext.use, .generalPurpose) - XCTAssert(connCreatedContext.eventLoop === el) - XCTAssertEqual(connections.startingGeneralPurposeConnections, 0) - } - - let connection = connections.leaseConnection(onPreferred: el1) - XCTAssertEqual(connection, .__testOnly_connection(id: 0, eventLoop: el1)) - } - - func testLeaseConnectionOnPreferredButUnavailableEL() { - let elg = EmbeddedEventLoopGroup(loops: 5) - let el1 = elg.next() - let el2 = elg.next() - let el3 = elg.next() - let el4 = elg.next() - let el5 = elg.next() - - var connections = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 8, - generator: .init(), - maximumConnectionUses: nil - ) - - for el in [el1, el2, el3, el4] { - XCTAssertEqual(connections.startingGeneralPurposeConnections, 0) - XCTAssertEqual(connections.startingEventLoopConnections(on: el), 0) - let connID = connections.createNewConnection(on: el) - XCTAssertEqual(connections.startingGeneralPurposeConnections, 1) - XCTAssertEqual(connections.startingEventLoopConnections(on: el), 0) - let conn: HTTPConnectionPool.Connection = .__testOnly_connection(id: connID, eventLoop: el) - let (_, connCreatedContext) = connections.newHTTP1ConnectionEstablished(conn) - XCTAssertEqual(connCreatedContext.use, .generalPurpose) - XCTAssert(connCreatedContext.eventLoop === el) - XCTAssertEqual(connections.startingGeneralPurposeConnections, 0) - } - - let connection = connections.leaseConnection(onPreferred: el5) - XCTAssertEqual(connection, .__testOnly_connection(id: 3, eventLoop: el4)) - } - - func testLeaseConnectionOnRequiredButUnavailableEL() { - let elg = EmbeddedEventLoopGroup(loops: 5) - let el1 = elg.next() - let el2 = elg.next() - let el3 = elg.next() - let el4 = elg.next() - let el5 = elg.next() - - var connections = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 8, - generator: .init(), - maximumConnectionUses: nil - ) - - for el in [el1, el2, el3, el4] { - XCTAssertEqual(connections.startingGeneralPurposeConnections, 0) - XCTAssertEqual(connections.startingEventLoopConnections(on: el), 0) - let connID = connections.createNewConnection(on: el) - XCTAssertEqual(connections.startingGeneralPurposeConnections, 1) - XCTAssertEqual(connections.startingEventLoopConnections(on: el), 0) - let conn: HTTPConnectionPool.Connection = .__testOnly_connection(id: connID, eventLoop: el) - let (_, connCreatedContext) = connections.newHTTP1ConnectionEstablished(conn) - XCTAssertEqual(connCreatedContext.use, .generalPurpose) - XCTAssert(connCreatedContext.eventLoop === el) - XCTAssertEqual(connections.startingGeneralPurposeConnections, 0) - } - - let connection = connections.leaseConnection(onRequired: el5) - XCTAssertEqual(connection, .none) - } - - func testLeaseConnectionOnRequiredAndAvailableEL() { - let elg = EmbeddedEventLoopGroup(loops: 2) - let el1 = elg.next() - let el2 = elg.next() - - var connections = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 8, - generator: .init(), - maximumConnectionUses: nil - ) - - for el in [el1, el1, el1, el1, el2] { - let connID = connections.createNewConnection(on: el) - let conn: HTTPConnectionPool.Connection = .__testOnly_connection(id: connID, eventLoop: el) - _ = connections.newHTTP1ConnectionEstablished(conn) - } - - // 1. get el from general pool, even though el is required! - guard let lease1 = connections.leaseConnection(onRequired: el1) else { - return XCTFail("Expected to get a connection at this point.") - } - // the last created connection on the correct el is the shortest amount idle. we should use this - XCTAssertEqual(lease1, .__testOnly_connection(id: 3, eventLoop: el1)) - _ = connections.releaseConnection(lease1.id) - - // 2. create specialized el connection - let connID5 = connections.createNewOverflowConnection(on: el1) - XCTAssertEqual(connections.startingEventLoopConnections(on: el1), 1) - let conn5: HTTPConnectionPool.Connection = .__testOnly_connection(id: connID5, eventLoop: el1) - _ = connections.newHTTP1ConnectionEstablished(conn5) - XCTAssertEqual(connections.startingEventLoopConnections(on: el1), 0) - - // 3. get el from specialized pool, since it is the newest! - guard let lease2 = connections.leaseConnection(onRequired: el1) else { - return XCTFail("Expected to get a connection at this point.") - } - XCTAssertEqual(lease2, conn5) - _ = connections.releaseConnection(lease2.id) - - // 4. create another general purpose connection on the correct el - let connID6 = connections.createNewConnection(on: el1) - let conn6: HTTPConnectionPool.Connection = .__testOnly_connection(id: connID6, eventLoop: el1) - _ = connections.newHTTP1ConnectionEstablished(conn6) - - // 5. get el from general pool, since it is the newest! - guard let lease3 = connections.leaseConnection(onRequired: el1) else { - return XCTFail("Expected to get a connection at this point.") - } - // the last created connection is the shortest amount idle. we should use this - XCTAssertEqual(lease3, conn6) - - _ = connections.releaseConnection(lease3.id) - } - - func testCloseConnectionIfIdle() { - let elg = EmbeddedEventLoopGroup(loops: 1) - var connections = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 8, - generator: .init(), - maximumConnectionUses: nil - ) - - let el1 = elg.next() - - // connection is idle - let conn1ID = connections.createNewConnection(on: el1) - let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1) - _ = connections.newHTTP1ConnectionEstablished(conn1) - XCTAssertEqual(connections.closeConnectionIfIdle(conn1ID), conn1) - - // connection is not idle - let conn2ID = connections.createNewConnection(on: el1) - let conn2: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn2ID, eventLoop: el1) - let (conn2Index, _) = connections.newHTTP1ConnectionEstablished(conn2) - XCTAssertEqual(connections.leaseConnection(at: conn2Index), conn2) - XCTAssertNil(connections.closeConnectionIfIdle(conn2ID)) - } - - func testCloseConnectionIfIdleButLeasedRaceCondition() { - let elg = EmbeddedEventLoopGroup(loops: 1) - var connections = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 8, - generator: .init(), - maximumConnectionUses: nil - ) - - let el1 = elg.next() - - // connection is idle - let connID = connections.createNewConnection(on: el1) - let conn: HTTPConnectionPool.Connection = .__testOnly_connection(id: connID, eventLoop: el1) - _ = connections.newHTTP1ConnectionEstablished(conn) - - // connection is leased - let lease = connections.leaseConnection(onPreferred: el1) - XCTAssertEqual(lease, conn) - - // timeout arrives minimal to late - XCTAssertEqual(connections.closeConnectionIfIdle(connID), nil) - } - - func testCloseConnectionIfIdleButClosedRaceCondition() { - let elg = EmbeddedEventLoopGroup(loops: 1) - var connections = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 8, - generator: .init(), - maximumConnectionUses: nil - ) - - let el1 = elg.next() - - // connection is idle - let connID = connections.createNewConnection(on: el1) - let conn: HTTPConnectionPool.Connection = .__testOnly_connection(id: connID, eventLoop: el1) - _ = connections.newHTTP1ConnectionEstablished(conn) - _ = connections.failConnection(connID) - - // timeout arrives minimal to late - XCTAssertEqual(connections.closeConnectionIfIdle(connID), nil) - } - - func testShutdown() { - let elg = EmbeddedEventLoopGroup(loops: 4) - let el1 = elg.next() - let el2 = elg.next() - let el3 = elg.next() - let el4 = elg.next() - - var connections = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 8, - generator: .init(), - maximumConnectionUses: nil - ) - - for el in [el1, el2, el3, el4] { - let connID = connections.createNewConnection(on: el) - let conn: HTTPConnectionPool.Connection = .__testOnly_connection(id: connID, eventLoop: el) - let (_, connContext) = connections.newHTTP1ConnectionEstablished(conn) - XCTAssertEqual(connContext.use, .generalPurpose) - XCTAssert(connContext.eventLoop === el) - } - - XCTAssertEqual(connections.stats.backingOff, 0) - XCTAssertEqual(connections.stats.leased, 0) - XCTAssertEqual(connections.stats.idle, 4) - - // connection is leased - guard let lease = connections.leaseConnection(onPreferred: el1) else { - return XCTFail("Expected to be able to lease a connection") - } - XCTAssertEqual(lease, .__testOnly_connection(id: 0, eventLoop: el1)) - - XCTAssertEqual(connections.stats.leased, 1) - XCTAssertEqual(connections.stats.idle, 3) - - // start another connection that fails - let backingOffID = connections.createNewConnection(on: el1) - XCTAssert(connections.backoffNextConnectionAttempt(backingOffID) === el1) - - // start another connection - let startingID = connections.createNewConnection(on: el2) - - let context = connections.shutdown() - XCTAssertEqual(context.close.count, 3) - XCTAssertEqual(context.cancel, [lease]) - XCTAssertEqual(context.connectBackoff, [backingOffID]) - - XCTAssertEqual(connections.stats.idle, 0) - XCTAssertEqual(connections.stats.backingOff, 0) - XCTAssertEqual(connections.stats.leased, 1) - XCTAssertEqual(connections.stats.connecting, 1) - XCTAssertFalse(connections.isEmpty) - - let (releaseIndex, _) = connections.releaseConnection(lease.id) - XCTAssertEqual(connections.closeConnection(at: releaseIndex), lease) - XCTAssertFalse(connections.isEmpty) - - guard let (failIndex, _) = connections.failConnection(startingID) else { - return XCTFail("Expected that the connection is remembered") - } - connections.removeConnection(at: failIndex) - XCTAssertTrue(connections.isEmpty) - } - - func testMigrationFromHTTP2() { - let elg = EmbeddedEventLoopGroup(loops: 4) - let generator = HTTPConnectionPool.Connection.ID.Generator() - var connections = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 8, - generator: generator, - maximumConnectionUses: nil - ) - - let el1 = elg.next() - let el2 = elg.next() - - let conn1ID = generator.next() - let conn2ID = generator.next() - - connections.migrateFromHTTP2( - starting: [(conn1ID, el1)], - backingOff: [(conn2ID, el2)] - ) - let newConnections = connections.createConnectionsAfterMigrationIfNeeded( - requiredEventLoopOfPendingRequests: [], - generalPurposeRequestCountGroupedByPreferredEventLoop: [(el1, 1), (el2, 1)] - ) - - XCTAssertTrue(newConnections.isEmpty) - - let stats = connections.stats - XCTAssertEqual(stats.idle, 0) - XCTAssertEqual(stats.leased, 0) - XCTAssertEqual(stats.connecting, 1) - XCTAssertEqual(stats.backingOff, 1) - } - - func testMigrationFromHTTP2WithPendingRequestsWithRequiredEventLoop() { - let elg = EmbeddedEventLoopGroup(loops: 4) - let generator = HTTPConnectionPool.Connection.ID.Generator() - var connections = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 8, - generator: generator, - maximumConnectionUses: nil - ) - - let el1 = elg.next() - let el2 = elg.next() - let el3 = elg.next() - - let conn1ID = generator.next() - let conn2ID = generator.next() - - connections.migrateFromHTTP2( - starting: [(conn1ID, el1)], - backingOff: [(conn2ID, el2)] - ) - let newConnections = connections.createConnectionsAfterMigrationIfNeeded( - requiredEventLoopOfPendingRequests: [(el3, 1)], - generalPurposeRequestCountGroupedByPreferredEventLoop: [] - ) - XCTAssertEqual(newConnections.count, 1) - XCTAssertEqual(newConnections.first?.1.id, el3.id) - - guard let conn3ID = newConnections.first?.0 else { - return XCTFail("expected to start a new connection") - } - - let stats = connections.stats - XCTAssertEqual(stats.idle, 0) - XCTAssertEqual(stats.leased, 0) - XCTAssertEqual(stats.connecting, 2) - XCTAssertEqual(stats.backingOff, 1) - - let conn3: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn3ID, eventLoop: el3) - let (_, context) = connections.newHTTP1ConnectionEstablished(conn3) - XCTAssertEqual(context.use, .eventLoop(el3)) - XCTAssertTrue(context.eventLoop === el3) - } - - func testMigrationFromHTTP2WithPendingRequestsWithRequiredEventLoopSameAsStartingConnections() { - let elg = EmbeddedEventLoopGroup(loops: 4) - let generator = HTTPConnectionPool.Connection.ID.Generator() - var connections = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 8, - generator: generator, - maximumConnectionUses: nil - ) - - let el1 = elg.next() - let el2 = elg.next() - - let conn1ID = generator.next() - let conn2ID = generator.next() - - connections.migrateFromHTTP2( - starting: [(conn1ID, el1)], - backingOff: [(conn2ID, el2)] - ) - - let stats = connections.stats - XCTAssertEqual(stats.idle, 0) - XCTAssertEqual(stats.leased, 0) - XCTAssertEqual(stats.connecting, 1) - XCTAssertEqual(stats.backingOff, 1) - - let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1) - let (_, context) = connections.newHTTP1ConnectionEstablished(conn1) - XCTAssertEqual(context.use, .generalPurpose) - XCTAssertTrue(context.eventLoop === el1) - } - - func testMigrationFromHTTP2WithPendingRequestsWithPreferredEventLoop() { - let elg = EmbeddedEventLoopGroup(loops: 4) - let generator = HTTPConnectionPool.Connection.ID.Generator() - var connections = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 8, - generator: generator, - maximumConnectionUses: nil - ) - - let el1 = elg.next() - let el2 = elg.next() - let el3 = elg.next() - - let conn1ID = generator.next() - let conn2ID = generator.next() - - connections.migrateFromHTTP2( - starting: [(conn1ID, el1)], - backingOff: [(conn2ID, el2)] - ) - let newConnections = connections.createConnectionsAfterMigrationIfNeeded( - requiredEventLoopOfPendingRequests: [], - generalPurposeRequestCountGroupedByPreferredEventLoop: [(el3, 3)] - ) - XCTAssertEqual(newConnections.count, 1) - XCTAssertEqual(newConnections.first?.1.id, el3.id) - - guard let conn3ID = newConnections.first?.0 else { - return XCTFail("expected to start a new connection") - } - - let stats = connections.stats - XCTAssertEqual(stats.idle, 0) - XCTAssertEqual(stats.leased, 0) - XCTAssertEqual(stats.connecting, 2) - XCTAssertEqual(stats.backingOff, 1) - - let conn3: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn3ID, eventLoop: el3) - let (_, context) = connections.newHTTP1ConnectionEstablished(conn3) - XCTAssertEqual(context.use, .generalPurpose) - XCTAssertTrue(context.eventLoop === el3) - } - - func testMigrationFromHTTP2WithAlreadyLeasedHTTP1Connection() { - let elg = EmbeddedEventLoopGroup(loops: 4) - let generator = HTTPConnectionPool.Connection.ID.Generator() - var connections = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 8, - generator: generator, - maximumConnectionUses: nil - ) - let el1 = elg.next() - let el2 = elg.next() - let el3 = elg.next() - - let conn1ID = connections.createNewConnection(on: el1) - let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1) - let (index, _) = connections.newHTTP1ConnectionEstablished(conn1) - _ = connections.leaseConnection(at: index) - - let conn2ID = generator.next() - let conn3ID = generator.next() - - connections.migrateFromHTTP2( - starting: [(conn2ID, el2)], - backingOff: [(conn3ID, el3)] - ) - let newConnections = connections.createConnectionsAfterMigrationIfNeeded( - requiredEventLoopOfPendingRequests: [], - generalPurposeRequestCountGroupedByPreferredEventLoop: [(el3, 3)] - ) - - XCTAssertEqual(newConnections.count, 1) - XCTAssertEqual(newConnections.first?.1.id, el3.id) - - guard let conn4ID = newConnections.first?.0 else { - return XCTFail("expected to start a new connection") - } - - let stats = connections.stats - XCTAssertEqual(stats.idle, 0) - XCTAssertEqual(stats.leased, 1) - XCTAssertEqual(stats.connecting, 2) - XCTAssertEqual(stats.backingOff, 1) - - let conn3: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn4ID, eventLoop: el3) - let (_, context) = connections.newHTTP1ConnectionEstablished(conn3) - XCTAssertEqual(context.use, .generalPurpose) - XCTAssertTrue(context.eventLoop === el3) - } - - func testMigrationFromHTTP2WithMoreStartingConnectionsThanMaximumAllowedConccurentConnections() { - let elg = EmbeddedEventLoopGroup(loops: 4) - let generator = HTTPConnectionPool.Connection.ID.Generator() - var connections = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 2, - generator: generator, - maximumConnectionUses: nil - ) - - let el1 = elg.next() - let el2 = elg.next() - let el3 = elg.next() - - let conn1ID = generator.next() - let conn2ID = generator.next() - let conn3ID = generator.next() - - connections.migrateFromHTTP2( - starting: [(conn1ID, el1), (conn2ID, el2), (conn3ID, el3)], - backingOff: [] - ) - - // first two connections should be added as general purpose connections - let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1) - let (_, context1) = connections.newHTTP1ConnectionEstablished(conn1) - XCTAssertEqual(context1.use, .generalPurpose) - XCTAssertTrue(context1.eventLoop === el1) - let conn2: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn2ID, eventLoop: el2) - let (_, context2) = connections.newHTTP1ConnectionEstablished(conn2) - XCTAssertEqual(context2.use, .generalPurpose) - XCTAssertTrue(context2.eventLoop === el2) - - // additional connection should be added as overflow connection - let conn3: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn3ID, eventLoop: el3) - let (_, context3) = connections.newHTTP1ConnectionEstablished(conn3) - XCTAssertEqual(context3.use, .eventLoop(el3)) - XCTAssertTrue(context3.eventLoop === el3) - } - - func testMigrationFromHTTP2StartsEnoghOverflowConnectionsForRequiredEventLoopRequests() { - let elg = EmbeddedEventLoopGroup(loops: 4) - let generator = HTTPConnectionPool.Connection.ID.Generator() - var connections = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 1, - generator: generator, - maximumConnectionUses: nil - ) - - let el1 = elg.next() - let el2 = elg.next() - let el3 = elg.next() - let el4 = elg.next() - - let conn1ID = generator.next() - let conn2ID = generator.next() - let conn3ID = generator.next() - - connections.migrateFromHTTP2( - starting: [(conn1ID, el1), (conn2ID, el2), (conn3ID, el3)], - backingOff: [] - ) - - let connectionsToCreate = connections.createConnectionsAfterMigrationIfNeeded( - requiredEventLoopOfPendingRequests: [(el2, 2), (el3, 1), (el4, 2)], - generalPurposeRequestCountGroupedByPreferredEventLoop: [] - ) - - XCTAssertEqual( - connectionsToCreate.map { $0.1.id }, - [el2.id, el4.id, el4.id], - "should create one connection for el2 and two for el4" - ) - - for (connID, el) in connectionsToCreate { - let conn: HTTPConnectionPool.Connection = .__testOnly_connection(id: connID, eventLoop: el) - let (_, context) = connections.newHTTP1ConnectionEstablished(conn) - XCTAssertEqual(context.use, .eventLoop(el)) - XCTAssertTrue(context.eventLoop === el) - } - } - - func testMigrationFromHTTP1ToHTTP2AndBackToHTTP1() throws { - let elg = EmbeddedEventLoopGroup(loops: 2) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - let el1 = elg.next() - let el2 = elg.next() - - let generator = HTTPConnectionPool.Connection.ID.Generator() - var connections = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 8, - generator: generator, - maximumConnectionUses: nil - ) - - let connID1 = connections.createNewConnection(on: el1) - - let context = connections.migrateToHTTP2() - XCTAssertEqual( - context, - .init( - backingOff: [], - starting: [(connID1, el1)], - close: [] - ) - ) - - let connID2 = generator.next() - - connections.migrateFromHTTP2( - starting: [(connID2, el2)], - backingOff: [] - ) - - let conn2: HTTPConnectionPool.Connection = .__testOnly_connection(id: connID2, eventLoop: el2) - let (_, idleContext) = connections.newHTTP1ConnectionEstablished(conn2) - XCTAssertEqual(idleContext.use, .generalPurpose) - XCTAssertEqual(idleContext.eventLoop.id, el2.id) - } -} - -extension HTTPConnectionPool.HTTP1Connections.HTTP1ToHTTP2MigrationContext: Equatable { - public static func == (lhs: Self, rhs: Self) -> Bool { - lhs.close == rhs.close && lhs.starting.elementsEqual(rhs.starting, by: { $0.0 == $1.0 && $0.1 === $1.1 }) - && lhs.backingOff.elementsEqual(rhs.backingOff, by: { $0.0 == $1.0 && $0.1 === $1.1 }) - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTPConnectionPool+HTTP1StateTests.swift b/Tests/AsyncHTTPClientTests/HTTPConnectionPool+HTTP1StateTests.swift deleted file mode 100644 index 2be6cfa26..000000000 --- a/Tests/AsyncHTTPClientTests/HTTPConnectionPool+HTTP1StateTests.swift +++ /dev/null @@ -1,884 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOEmbedded -import NIOHTTP1 -import NIOPosix -import XCTest - -@testable import AsyncHTTPClient - -class HTTPConnectionPool_HTTP1StateMachineTests: XCTestCase { - func testCreatingAndFailingConnections() { - struct SomeError: Error, Equatable {} - let elg = EmbeddedEventLoopGroup(loops: 4) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - - var state = HTTPConnectionPool.StateMachine( - idGenerator: .init(), - maximumConcurrentHTTP1Connections: 8, - retryConnectionEstablishment: true, - preferHTTP1: true, - maximumConnectionUses: nil - ) - - var connections = MockConnectionPool() - var queuer = MockRequestQueuer() - - // for the first eight requests, the pool should try to create new connections. - - for _ in 0..<8 { - let mockRequest = MockHTTPScheduableRequest(eventLoop: elg.next()) - let request = HTTPConnectionPool.Request(mockRequest) - let action = state.executeRequest(request) - guard case .createConnection(let connectionID, let connectionEL) = action.connection else { - return XCTFail("Unexpected connection action") - } - XCTAssertEqual(.scheduleRequestTimeout(for: request, on: mockRequest.eventLoop), action.request) - XCTAssert(connectionEL === mockRequest.eventLoop) - - XCTAssertNoThrow(try connections.createConnection(connectionID, on: connectionEL)) - XCTAssertNoThrow(try queuer.queue(mockRequest, id: request.id)) - } - - // the next eight requests should only be queued. - - for _ in 0..<8 { - let mockRequest = MockHTTPScheduableRequest(eventLoop: elg.next()) - let request = HTTPConnectionPool.Request(mockRequest) - let action = state.executeRequest(request) - guard case .none = action.connection else { - return XCTFail("Unexpected connection action") - } - XCTAssertEqual(.scheduleRequestTimeout(for: request, on: mockRequest.eventLoop), action.request) - XCTAssertNoThrow(try queuer.queue(mockRequest, id: request.id)) - } - - // timeout all queued requests except for two - - // fail all connection attempts - while let randomConnectionID = connections.randomStartingConnection() { - XCTAssertNoThrow(try connections.failConnectionCreation(randomConnectionID)) - let action = state.failedToCreateNewConnection(SomeError(), connectionID: randomConnectionID) - - // After a failed connection attempt, must not fail a request. Instead we should retry - // to create the connection with a backoff and a small jitter. The request should only - // be failed, once the connection setup timeout is hit or the request reaches it - // deadline. - - XCTAssertEqual(action.request, .none) - - guard case .scheduleBackoffTimer(randomConnectionID, backoff: _, on: _) = action.connection else { - return XCTFail("Unexpected request action: \(action.request)") - } - - XCTAssertNoThrow(try connections.startConnectionBackoffTimer(randomConnectionID)) - } - - // cancel all queued requests - while let request = queuer.timeoutRandomRequest() { - let cancelAction = state.cancelRequest(request.0) - XCTAssertEqual(cancelAction.connection, .none) - XCTAssertEqual(cancelAction.request, .failRequest(.init(request.1), SomeError(), cancelTimeout: true)) - } - - // connection backoff done - while let connectionID = connections.randomBackingOffConnection() { - XCTAssertNoThrow(try connections.connectionBackoffTimerDone(connectionID)) - let backoffAction = state.connectionCreationBackoffDone(connectionID) - XCTAssertEqual(backoffAction.connection, .none) - XCTAssertEqual(backoffAction.request, .none) - } - - XCTAssert(queuer.isEmpty) - XCTAssert(connections.isEmpty) - } - - func testCreatingAndFailingConnectionsWithoutRetry() { - struct SomeError: Error, Equatable {} - let elg = EmbeddedEventLoopGroup(loops: 4) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - - var state = HTTPConnectionPool.StateMachine( - idGenerator: .init(), - maximumConcurrentHTTP1Connections: 8, - retryConnectionEstablishment: false, - preferHTTP1: true, - maximumConnectionUses: nil - ) - - var connections = MockConnectionPool() - var queuer = MockRequestQueuer() - - // for the first eight requests, the pool should try to create new connections. - - for _ in 0..<8 { - let mockRequest = MockHTTPScheduableRequest(eventLoop: elg.next()) - let request = HTTPConnectionPool.Request(mockRequest) - let action = state.executeRequest(request) - guard case .createConnection(let connectionID, let connectionEL) = action.connection else { - return XCTFail("Unexpected connection action") - } - XCTAssertEqual(.scheduleRequestTimeout(for: request, on: mockRequest.eventLoop), action.request) - XCTAssert(connectionEL === mockRequest.eventLoop) - - XCTAssertNoThrow(try connections.createConnection(connectionID, on: connectionEL)) - XCTAssertNoThrow(try queuer.queue(mockRequest, id: request.id)) - } - - // the next eight requests should only be queued. - - for _ in 0..<8 { - let mockRequest = MockHTTPScheduableRequest(eventLoop: elg.next()) - let request = HTTPConnectionPool.Request(mockRequest) - let action = state.executeRequest(request) - guard case .none = action.connection else { - return XCTFail("Unexpected connection action") - } - XCTAssertEqual(.scheduleRequestTimeout(for: request, on: mockRequest.eventLoop), action.request) - XCTAssertNoThrow(try queuer.queue(mockRequest, id: request.id)) - } - - // the first failure should cancel all requests because we have disabled connection establishtment retry - let randomConnectionID = connections.randomStartingConnection()! - XCTAssertNoThrow(try connections.failConnectionCreation(randomConnectionID)) - let action = state.failedToCreateNewConnection(SomeError(), connectionID: randomConnectionID) - XCTAssertEqual(action.connection, .none) - guard case .failRequestsAndCancelTimeouts(let requestsToFail, let requestError) = action.request else { - return XCTFail("Unexpected request action: \(action.request)") - } - XCTAssertEqualTypeAndValue(requestError, SomeError()) - for requestToFail in requestsToFail { - XCTAssertNoThrow(try queuer.fail(requestToFail.id, request: requestToFail.__testOnly_wrapped_request())) - } - - // all requests have been canceled and therefore nothing should happen if a connection fails - while let randomConnectionID = connections.randomStartingConnection() { - XCTAssertNoThrow(try connections.failConnectionCreation(randomConnectionID)) - let action = state.failedToCreateNewConnection(SomeError(), connectionID: randomConnectionID) - - XCTAssertEqual(action, .none) - } - - XCTAssert(queuer.isEmpty) - XCTAssert(connections.isEmpty) - } - - func testConnectionFailureBackoff() { - let elg = EmbeddedEventLoopGroup(loops: 4) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - - var state = HTTPConnectionPool.StateMachine( - idGenerator: .init(), - maximumConcurrentHTTP1Connections: 2, - retryConnectionEstablishment: true, - preferHTTP1: true, - maximumConnectionUses: nil - ) - - let mockRequest = MockHTTPScheduableRequest(eventLoop: elg.next()) - let request = HTTPConnectionPool.Request(mockRequest) - - let action = state.executeRequest(request) - XCTAssertEqual(.scheduleRequestTimeout(for: request, on: mockRequest.eventLoop), action.request) - - // 1. connection attempt - guard case .createConnection(let connectionID, on: let connectionEL) = action.connection else { - return XCTFail("Unexpected connection action: \(action.connection)") - } - XCTAssert(connectionEL === mockRequest.eventLoop) // XCTAssertIdentical not available on Linux - - let failedConnect1 = state.failedToCreateNewConnection( - HTTPClientError.connectTimeout, - connectionID: connectionID - ) - XCTAssertEqual(failedConnect1.request, .none) - guard case .scheduleBackoffTimer(connectionID, let backoffTimeAmount1, _) = failedConnect1.connection else { - return XCTFail("Unexpected connection action: \(failedConnect1.connection)") - } - - // 2. connection attempt - let backoffDoneAction = state.connectionCreationBackoffDone(connectionID) - XCTAssertEqual(backoffDoneAction.request, .none) - guard case .createConnection(let newConnectionID, on: let newEventLoop) = backoffDoneAction.connection else { - return XCTFail("Unexpected connection action: \(backoffDoneAction.connection)") - } - XCTAssertGreaterThan(newConnectionID, connectionID) - XCTAssert(connectionEL === newEventLoop) // XCTAssertIdentical not available on Linux - - let failedConnect2 = state.failedToCreateNewConnection( - HTTPClientError.connectTimeout, - connectionID: newConnectionID - ) - XCTAssertEqual(failedConnect2.request, .none) - guard case .scheduleBackoffTimer(newConnectionID, let backoffTimeAmount2, _) = failedConnect2.connection else { - return XCTFail("Unexpected connection action: \(failedConnect2.connection)") - } - - XCTAssertNotEqual(backoffTimeAmount2, backoffTimeAmount1) - - // 3. request times out - let failRequest = state.timeoutRequest(request.id) - guard case .failRequest(let requestToFail, let requestError, cancelTimeout: false) = failRequest.request else { - return XCTFail("Unexpected request action: \(action.request)") - } - - // XCTAssertIdentical not available on Linux - XCTAssert(requestToFail.__testOnly_wrapped_request() === mockRequest) - XCTAssertEqual(requestError as? HTTPClientError, .connectTimeout) - XCTAssertEqual(failRequest.connection, .none) - - // 4. retry connection, but no more queued requests. - XCTAssertEqual(state.connectionCreationBackoffDone(newConnectionID), .none) - } - - func testCancelRequestWorks() { - let elg = EmbeddedEventLoopGroup(loops: 4) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - - var state = HTTPConnectionPool.StateMachine( - idGenerator: .init(), - maximumConcurrentHTTP1Connections: 2, - retryConnectionEstablishment: true, - preferHTTP1: true, - maximumConnectionUses: nil - ) - - let mockRequest = MockHTTPScheduableRequest(eventLoop: elg.next()) - let request = HTTPConnectionPool.Request(mockRequest) - - let executeAction = state.executeRequest(request) - XCTAssertEqual(.scheduleRequestTimeout(for: request, on: mockRequest.eventLoop), executeAction.request) - - // 1. connection attempt - guard case .createConnection(let connectionID, on: let connectionEL) = executeAction.connection else { - return XCTFail("Unexpected connection action: \(executeAction.connection)") - } - XCTAssert(connectionEL === mockRequest.eventLoop) // XCTAssertIdentical not available on Linux - - // 2. cancel request - - let cancelAction = state.cancelRequest(request.id) - XCTAssertEqual(cancelAction.request, .failRequest(request, HTTPClientError.cancelled, cancelTimeout: true)) - XCTAssertEqual(cancelAction.connection, .none) - - // 3. request timeout triggers to late - XCTAssertEqual(state.timeoutRequest(request.id), .none, "To late timeout is ignored") - - // 4. succeed connection attempt - let connectedAction = state.newHTTP1ConnectionCreated( - .__testOnly_connection(id: connectionID, eventLoop: connectionEL) - ) - XCTAssertEqual(connectedAction.request, .none, "Request must not be executed") - XCTAssertEqual(connectedAction.connection, .scheduleTimeoutTimer(connectionID, on: connectionEL)) - } - - func testExecuteOnShuttingDownPool() { - let elg = EmbeddedEventLoopGroup(loops: 4) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - - var state = HTTPConnectionPool.StateMachine( - idGenerator: .init(), - maximumConcurrentHTTP1Connections: 2, - retryConnectionEstablishment: true, - preferHTTP1: true, - maximumConnectionUses: nil - ) - - let mockRequest = MockHTTPScheduableRequest(eventLoop: elg.next()) - let request = HTTPConnectionPool.Request(mockRequest) - - let executeAction = state.executeRequest(request) - XCTAssertEqual(.scheduleRequestTimeout(for: request, on: mockRequest.eventLoop), executeAction.request) - - // 1. connection attempt - guard case .createConnection(let connectionID, on: let connectionEL) = executeAction.connection else { - return XCTFail("Unexpected connection action: \(executeAction.connection)") - } - XCTAssert(connectionEL === mockRequest.eventLoop) // XCTAssertIdentical not available on Linux - - // 2. connection succeeds - let connection: HTTPConnectionPool.Connection = .__testOnly_connection( - id: connectionID, - eventLoop: connectionEL - ) - let connectedAction = state.newHTTP1ConnectionCreated(connection) - guard case .executeRequest(request, connection, cancelTimeout: true) = connectedAction.request else { - return XCTFail("Unexpected request action: \(connectedAction.request)") - } - XCTAssert(request.__testOnly_wrapped_request() === mockRequest) // XCTAssertIdentical not available on Linux - XCTAssertEqual(connectedAction.connection, .none) - - // 3. shutdown - let shutdownAction = state.shutdown() - XCTAssertEqual(.none, shutdownAction.request) - guard case .cleanupConnections(let cleanupContext, isShutdown: .no) = shutdownAction.connection else { - return XCTFail("Unexpected connection action: \(shutdownAction.connection)") - } - - XCTAssertEqual(cleanupContext.cancel.count, 1) - XCTAssertEqual(cleanupContext.cancel.first?.id, connectionID) - XCTAssertEqual(cleanupContext.close, []) - XCTAssertEqual(cleanupContext.connectBackoff, []) - - // 4. execute another request - let finalMockRequest = MockHTTPScheduableRequest(eventLoop: elg.next()) - let finalRequest = HTTPConnectionPool.Request(finalMockRequest) - let failAction = state.executeRequest(finalRequest) - XCTAssertEqual(failAction.connection, .none) - XCTAssertEqual( - failAction.request, - .failRequest(finalRequest, HTTPClientError.alreadyShutdown, cancelTimeout: false) - ) - - // 5. close open connection - let closeAction = state.http1ConnectionClosed(connectionID) - XCTAssertEqual(closeAction.connection, .cleanupConnections(.init(), isShutdown: .yes(unclean: true))) - XCTAssertEqual(closeAction.request, .none) - } - - func testRequestsAreQueuedIfAllConnectionsAreInUseAndRequestsAreDequeuedInOrder() { - let elg = EmbeddedEventLoopGroup(loops: 4) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - - guard var (connections, state) = try? MockConnectionPool.http1(elg: elg, numberOfConnections: 8) else { - return XCTFail("Test setup failed") - } - - XCTAssertEqual(connections.parked, 8) - - // Add eight requests to fill all connections - for _ in 0..<8 { - let eventLoop = elg.next() - guard - let expectedConnection = connections.newestParkedConnection(for: eventLoop) - ?? connections.newestParkedConnection - else { - return XCTFail("Expected to still have connections available") - } - - let mockRequest = MockHTTPScheduableRequest(eventLoop: eventLoop) - let request = HTTPConnectionPool.Request(mockRequest) - let action = state.executeRequest(request) - - XCTAssertEqual(action.connection, .cancelTimeoutTimer(expectedConnection.id)) - guard case .executeRequest(let returnedRequest, expectedConnection, cancelTimeout: false) = action.request - else { - return XCTFail("Expected to execute a request next, but got: \(action.request)") - } - - XCTAssert(mockRequest === returnedRequest.__testOnly_wrapped_request()) - - XCTAssertNoThrow(try connections.activateConnection(expectedConnection.id)) - XCTAssertNoThrow(try connections.execute(mockRequest, on: expectedConnection)) - } - - // Add 100 requests to fill request queue - var queuedRequestsOrder = CircularBuffer() - var queuer = MockRequestQueuer() - for _ in 0..<100 { - let eventLoop = elg.next() - let mockRequest = MockHTTPScheduableRequest(eventLoop: eventLoop, requiresEventLoopForChannel: false) - let request = HTTPConnectionPool.Request(mockRequest) - let action = state.executeRequest(request) - - XCTAssertEqual(action.connection, .none) - XCTAssertEqual(.scheduleRequestTimeout(for: request, on: mockRequest.eventLoop), action.request) - - XCTAssertNoThrow(try queuer.queue(mockRequest, id: request.id)) - queuedRequestsOrder.append(request.id) - } - - while let connection = connections.randomLeasedConnection() { - XCTAssertNoThrow(try connections.finishExecution(connection.id)) - let action = state.http1ConnectionReleased(connection.id) - - switch action.connection { - case .scheduleTimeoutTimer(connection.id, on: let timerEL): - // if all queued requests are processed, the connection will be parked - XCTAssert(queuedRequestsOrder.isEmpty) - XCTAssertEqual(action.request, .none) - XCTAssert(connection.eventLoop === timerEL) - XCTAssertNoThrow(try connections.parkConnection(connection.id)) - case .none: - guard case .executeRequest(let request, connection, cancelTimeout: true) = action.request else { - return XCTFail("Unexpected request action: \(action.request)") - } - XCTAssertEqual(request.id, queuedRequestsOrder.popFirst()) - let mockRequest = request.__testOnly_wrapped_request() - XCTAssertNoThrow(try connections.execute(queuer.get(request.id, request: mockRequest), on: connection)) - - default: - XCTFail("Unexpected connection action: \(action)") - } - } - - XCTAssertEqual(connections.parked, 8) - XCTAssert(queuer.isEmpty) - } - - func testBestConnectionIsPicked() { - let elg = MultiThreadedEventLoopGroup(numberOfThreads: 64) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - - guard var (connections, state) = try? MockConnectionPool.http1(elg: elg, numberOfConnections: 8) else { - return XCTFail("Test setup failed") - } - - for index in 1...300 { - // Every iteration we start with eight parked connections - XCTAssertEqual(connections.parked, 8) - - var reqEventLoop: EventLoop = elg.next() - for _ in 0..<((0..<63).randomElement()!) { - // pick a random eventLoop for the next request - reqEventLoop = elg.next() - } - - // 10% of the cases enforce the eventLoop - let elRequired = (0..<10).randomElement().flatMap { $0 == 0 ? true : false }! - let mockRequest = MockHTTPScheduableRequest( - eventLoop: reqEventLoop, - requiresEventLoopForChannel: elRequired - ) - let request = HTTPConnectionPool.Request(mockRequest) - - let action = state.executeRequest(request) - - switch action.connection { - case .createConnection(let connectionID, on: let connEventLoop): - XCTAssertTrue(elRequired) - XCTAssertNil(connections.newestParkedConnection(for: reqEventLoop)) - XCTAssert(connEventLoop === reqEventLoop) - XCTAssertEqual(action.request, .scheduleRequestTimeout(for: request, on: reqEventLoop)) - - let connection: HTTPConnectionPool.Connection = .__testOnly_connection( - id: connectionID, - eventLoop: connEventLoop - ) - let createdAction = state.newHTTP1ConnectionCreated(connection) - XCTAssertEqual(createdAction.request, .executeRequest(request, connection, cancelTimeout: true)) - XCTAssertEqual(createdAction.connection, .none) - - let doneAction = state.http1ConnectionReleased(connectionID) - XCTAssertEqual(doneAction.request, .none) - XCTAssertEqual(doneAction.connection, .closeConnection(connection, isShutdown: .no)) - XCTAssertEqual(state.http1ConnectionClosed(connectionID), .none) - - case .cancelTimeoutTimer(let connectionID): - guard - let expectedConnection = connections.newestParkedConnection(for: reqEventLoop) - ?? connections.newestParkedConnection - else { - return XCTFail("Expected to have connections available") - } - - if elRequired { - XCTAssert(expectedConnection.eventLoop === reqEventLoop) - } - - XCTAssertEqual( - connectionID, - expectedConnection.id, - "Request is scheduled on the connection we expected" - ) - XCTAssertNoThrow(try connections.activateConnection(connectionID)) - - guard case .executeRequest(let request, let connection, cancelTimeout: false) = action.request else { - return XCTFail("Expected to execute a request, but got: \(action.request)") - } - XCTAssertEqual(connection, expectedConnection) - XCTAssertNoThrow(try connections.execute(request.__testOnly_wrapped_request(), on: connection)) - XCTAssertNoThrow(try connections.finishExecution(connection.id)) - - XCTAssertEqual( - state.http1ConnectionReleased(connection.id), - .init(request: .none, connection: .scheduleTimeoutTimer(connection.id, on: connection.eventLoop)) - ) - XCTAssertNoThrow(try connections.parkConnection(connectionID)) - - default: - XCTFail("Unexpected connection action in iteration \(index): \(action.connection)") - } - } - - XCTAssertEqual(connections.parked, 8) - } - - func testConnectionAbortIsIgnoredIfThereAreNoQueuedRequests() { - let elg = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - - guard var (connections, state) = try? MockConnectionPool.http1(elg: elg, numberOfConnections: 8) else { - return XCTFail("Test setup failed") - } - - XCTAssertEqual(connections.parked, 8) - - // close a leased connection == abort - let mockRequest = MockHTTPScheduableRequest(eventLoop: elg.next()) - let request = HTTPConnectionPool.Request(mockRequest) - guard let connectionToAbort = connections.newestParkedConnection else { - return XCTFail("Expected to have a parked connection") - } - let action = state.executeRequest(request) - XCTAssertEqual(action.connection, .cancelTimeoutTimer(connectionToAbort.id)) - XCTAssertNoThrow(try connections.activateConnection(connectionToAbort.id)) - XCTAssertEqual(action.request, .executeRequest(request, connectionToAbort, cancelTimeout: false)) - XCTAssertNoThrow(try connections.execute(mockRequest, on: connectionToAbort)) - XCTAssertEqual(connections.parked, 7) - XCTAssertEqual(connections.used, 1) - XCTAssertNoThrow(try connections.abortConnection(connectionToAbort.id)) - XCTAssertEqual(state.http1ConnectionClosed(connectionToAbort.id), .none) - XCTAssertEqual(connections.parked, 7) - XCTAssertEqual(connections.used, 0) - } - - func testConnectionCloseLeadsToTumbleWeedIfThereNoQueuedRequests() { - let elg = EmbeddedEventLoopGroup(loops: 1) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - - guard var (connections, state) = try? MockConnectionPool.http1(elg: elg, numberOfConnections: 8) else { - return XCTFail("Test setup failed") - } - - XCTAssertEqual(connections.parked, 8) - - // close a parked connection - guard let connectionToClose = connections.randomParkedConnection() else { - return XCTFail("Expected to have a parked connection") - } - XCTAssertNoThrow(try connections.closeConnection(connectionToClose)) - XCTAssertEqual(state.http1ConnectionClosed(connectionToClose.id), .none) - XCTAssertEqual(connections.parked, 7) - } - - func testConnectionAbortLeadsToNewConnectionsIfThereAreQueuedRequests() { - let elg = EmbeddedEventLoopGroup(loops: 8) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - - guard var (connections, state) = try? MockConnectionPool.http1(elg: elg, numberOfConnections: 8) else { - return XCTFail("Test setup failed") - } - - XCTAssertEqual(connections.parked, 8) - - // Add eight requests to fill all connections - for _ in 0..<8 { - let eventLoop = elg.next() - guard - let expectedConnection = connections.newestParkedConnection(for: eventLoop) - ?? connections.newestParkedConnection - else { - return XCTFail("Expected to still have connections available") - } - - let mockRequest = MockHTTPScheduableRequest(eventLoop: eventLoop) - let request = HTTPConnectionPool.Request(mockRequest) - let action = state.executeRequest(request) - - XCTAssertEqual(action.connection, .cancelTimeoutTimer(expectedConnection.id)) - XCTAssertEqual(action.request, .executeRequest(request, expectedConnection, cancelTimeout: false)) - - XCTAssertNoThrow(try connections.activateConnection(expectedConnection.id)) - XCTAssertNoThrow(try connections.execute(mockRequest, on: expectedConnection)) - } - - // Add 100 requests to fill request queue - var queuedRequestsOrder = CircularBuffer() - var queuer = MockRequestQueuer() - for _ in 0..<100 { - let eventLoop = elg.next() - - let mockRequest = MockHTTPScheduableRequest(eventLoop: eventLoop, requiresEventLoopForChannel: false) - let request = HTTPConnectionPool.Request(mockRequest) - let action = state.executeRequest(request) - - XCTAssertEqual(.none, action.connection) - XCTAssertEqual(.scheduleRequestTimeout(for: request, on: mockRequest.eventLoop), action.request) - XCTAssertNoThrow(try queuer.queue(mockRequest, id: request.id)) - queuedRequestsOrder.append(request.id) - } - - while let closedConnection = connections.randomLeasedConnection() { - XCTAssertNoThrow(try connections.abortConnection(closedConnection.id)) - XCTAssertEqual(connections.parked, 0) - let action = state.http1ConnectionClosed(closedConnection.id) - - switch action.connection { - case .createConnection(let newConnectionID, on: let eventLoop): - XCTAssertEqual(action.request, .none) - XCTAssertNoThrow(try connections.createConnection(newConnectionID, on: eventLoop)) - XCTAssertEqual(connections.starting, 1) - - var maybeNewConnection: HTTPConnectionPool.Connection? - XCTAssertNoThrow(maybeNewConnection = try connections.succeedConnectionCreationHTTP1(newConnectionID)) - guard let newConnection = maybeNewConnection else { return XCTFail("Expected to get a new connection") } - let afterRecreationAction = state.newHTTP1ConnectionCreated(newConnection) - XCTAssertEqual(afterRecreationAction.connection, .none) - guard - case .executeRequest(let request, newConnection, cancelTimeout: true) = afterRecreationAction - .request - else { - return XCTFail("Unexpected request action: \(action.request)") - } - - XCTAssertEqual(request.id, queuedRequestsOrder.popFirst()) - XCTAssertNoThrow( - try connections.execute( - queuer.get(request.id, request: request.__testOnly_wrapped_request()), - on: newConnection - ) - ) - - case .none: - XCTAssert(queuer.isEmpty) - default: - XCTFail("Unexpected connection action: \(action.connection)") - } - } - } - - func testParkedConnectionTimesOut() { - let elg = EmbeddedEventLoopGroup(loops: 1) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - - guard var (connections, state) = try? MockConnectionPool.http1(elg: elg, numberOfConnections: 1) else { - return XCTFail("Test setup failed") - } - - guard let connection = connections.randomParkedConnection() else { - return XCTFail("Expected to have one parked connection") - } - - let action = state.connectionIdleTimeout(connection.id) - XCTAssertEqual(action.connection, .closeConnection(connection, isShutdown: .no)) - XCTAssertEqual(action.request, .none) - XCTAssertNoThrow(try connections.closeConnection(connection)) - } - - func testConnectionPoolFullOfParkedConnectionsIsShutdownImmediately() { - let elg = EmbeddedEventLoopGroup(loops: 8) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - - guard var (connections, state) = try? MockConnectionPool.http1(elg: elg, numberOfConnections: 8) else { - return XCTFail("Test setup failed") - } - - XCTAssertEqual(connections.parked, 8) - let action = state.shutdown() - XCTAssertEqual(.none, action.request) - - guard case .cleanupConnections(let closeContext, isShutdown: .yes(unclean: false)) = action.connection else { - return XCTFail("Unexpected connection event: \(action.connection)") - } - - XCTAssertEqual(closeContext.close.count, 8) - - for connection in closeContext.close { - XCTAssertNoThrow(try connections.closeConnection(connection)) - } - - XCTAssertEqual(connections.count, 0) - } - - func testParkedConnectionTimesOutButIsAlsoClosedByRemote() { - let elg = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - - guard var (connections, state) = try? MockConnectionPool.http1(elg: elg, numberOfConnections: 1) else { - return XCTFail("Test setup failed") - } - - guard let connection = connections.randomParkedConnection() else { - return XCTFail("Expected to have one parked connection") - } - - // triggered by remote peer - XCTAssertNoThrow(try connections.abortConnection(connection.id)) - XCTAssertEqual(state.http1ConnectionClosed(connection.id), .none) - - // triggered by timer - XCTAssertEqual(state.connectionIdleTimeout(connection.id), .none) - } - - func testConnectionBackoffVsShutdownRace() { - let elg = EmbeddedEventLoopGroup(loops: 2) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - - var state = HTTPConnectionPool.StateMachine( - idGenerator: .init(), - maximumConcurrentHTTP1Connections: 6, - retryConnectionEstablishment: true, - preferHTTP1: true, - maximumConnectionUses: nil - ) - - let mockRequest = MockHTTPScheduableRequest(eventLoop: elg.next(), requiresEventLoopForChannel: false) - let request = HTTPConnectionPool.Request(mockRequest) - - let executeAction = state.executeRequest(request) - guard case .createConnection(let connectionID, on: let connEL) = executeAction.connection else { - return XCTFail("Expected to create a connection") - } - - XCTAssertEqual(executeAction.request, .scheduleRequestTimeout(for: request, on: mockRequest.eventLoop)) - - let failAction = state.failedToCreateNewConnection(HTTPClientError.cancelled, connectionID: connectionID) - guard case .scheduleBackoffTimer(connectionID, backoff: _, on: let timerEL) = failAction.connection else { - return XCTFail("Expected to create a backoff timer") - } - XCTAssert(timerEL === connEL) - XCTAssertEqual(failAction.request, .none) - - let shutdownAction = state.shutdown() - guard case .cleanupConnections(let context, isShutdown: .yes(unclean: true)) = shutdownAction.connection else { - return XCTFail("Expected to cleanup") - } - XCTAssertEqual(context.close.count, 0) - XCTAssertEqual(context.cancel.count, 0) - XCTAssertEqual(context.connectBackoff, [connectionID]) - XCTAssertEqual(shutdownAction.request, .failRequestsAndCancelTimeouts([request], HTTPClientError.cancelled)) - - XCTAssertEqual(state.connectionCreationBackoffDone(connectionID), .none) - } - - func testRequestThatTimesOutIsFailedWithLastConnectionCreationError() { - let elg = EmbeddedEventLoopGroup(loops: 1) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - - var state = HTTPConnectionPool.StateMachine( - idGenerator: .init(), - maximumConcurrentHTTP1Connections: 6, - retryConnectionEstablishment: true, - preferHTTP1: true, - maximumConnectionUses: nil - ) - - let mockRequest = MockHTTPScheduableRequest(eventLoop: elg.next(), requiresEventLoopForChannel: false) - let request = HTTPConnectionPool.Request(mockRequest) - - let executeAction = state.executeRequest(request) - guard case .createConnection(let connectionID, on: let connEL) = executeAction.connection else { - return XCTFail("Expected to create a connection") - } - - XCTAssertEqual(executeAction.request, .scheduleRequestTimeout(for: request, on: mockRequest.eventLoop)) - - let failAction = state.failedToCreateNewConnection( - HTTPClientError.httpProxyHandshakeTimeout, - connectionID: connectionID - ) - guard case .scheduleBackoffTimer(connectionID, backoff: _, on: let timerEL) = failAction.connection else { - return XCTFail("Expected to create a backoff timer") - } - XCTAssert(timerEL === connEL) - XCTAssertEqual(failAction.request, .none) - - let timeoutAction = state.timeoutRequest(request.id) - XCTAssertEqual( - timeoutAction.request, - .failRequest(request, HTTPClientError.httpProxyHandshakeTimeout, cancelTimeout: false) - ) - XCTAssertEqual(timeoutAction.connection, .none) - } - - func testRequestThatTimesOutBeforeAConnectionIsEstablishedIsFailedWithConnectTimeoutError() { - let eventLoop = EmbeddedEventLoop() - defer { XCTAssertNoThrow(try eventLoop.syncShutdownGracefully()) } - - var state = HTTPConnectionPool.StateMachine( - idGenerator: .init(), - maximumConcurrentHTTP1Connections: 6, - retryConnectionEstablishment: true, - preferHTTP1: true, - maximumConnectionUses: nil - ) - - let mockRequest = MockHTTPScheduableRequest(eventLoop: eventLoop.next(), requiresEventLoopForChannel: false) - let request = HTTPConnectionPool.Request(mockRequest) - - let executeAction = state.executeRequest(request) - guard case .createConnection(_, on: _) = executeAction.connection else { - return XCTFail("Expected to create a connection") - } - XCTAssertEqual(executeAction.request, .scheduleRequestTimeout(for: request, on: mockRequest.eventLoop)) - - let timeoutAction = state.timeoutRequest(request.id) - XCTAssertEqual( - timeoutAction.request, - .failRequest(request, HTTPClientError.connectTimeout, cancelTimeout: false) - ) - XCTAssertEqual(timeoutAction.connection, .none) - } - - func testRequestThatTimesOutAfterAConnectionWasEstablishedSuccessfullyTimesOutWithGenericError() { - let elg = EmbeddedEventLoopGroup(loops: 1) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - - var state = HTTPConnectionPool.StateMachine( - idGenerator: .init(), - maximumConcurrentHTTP1Connections: 6, - retryConnectionEstablishment: true, - preferHTTP1: true, - maximumConnectionUses: nil - ) - - let mockRequest1 = MockHTTPScheduableRequest(eventLoop: elg.next(), requiresEventLoopForChannel: false) - let request1 = HTTPConnectionPool.Request(mockRequest1) - - let executeAction1 = state.executeRequest(request1) - guard case .createConnection(let connectionID1, on: let connEL1) = executeAction1.connection else { - return XCTFail("Expected to create a connection") - } - XCTAssert(mockRequest1.eventLoop === connEL1) - - XCTAssertEqual(executeAction1.request, .scheduleRequestTimeout(for: request1, on: mockRequest1.eventLoop)) - - let mockRequest2 = MockHTTPScheduableRequest(eventLoop: elg.next(), requiresEventLoopForChannel: false) - let request2 = HTTPConnectionPool.Request(mockRequest2) - - let executeAction2 = state.executeRequest(request2) - guard case .createConnection(let connectionID2, on: let connEL2) = executeAction2.connection else { - return XCTFail("Expected to create a connection") - } - XCTAssert(mockRequest2.eventLoop === connEL2) - - XCTAssertEqual(executeAction2.request, .scheduleRequestTimeout(for: request2, on: connEL1)) - - let failAction = state.failedToCreateNewConnection( - HTTPClientError.httpProxyHandshakeTimeout, - connectionID: connectionID1 - ) - guard case .scheduleBackoffTimer(connectionID1, backoff: _, on: let timerEL) = failAction.connection else { - return XCTFail("Expected to create a backoff timer") - } - XCTAssert(timerEL === connEL2) - XCTAssertEqual(failAction.request, .none) - - let conn2 = HTTPConnectionPool.Connection.__testOnly_connection(id: connectionID2, eventLoop: connEL2) - let createdAction = state.newHTTP1ConnectionCreated(conn2) - - XCTAssertEqual(createdAction.request, .executeRequest(request1, conn2, cancelTimeout: true)) - XCTAssertEqual(createdAction.connection, .none) - - let timeoutAction = state.timeoutRequest(request2.id) - XCTAssertEqual( - timeoutAction.request, - .failRequest(request2, HTTPClientError.getConnectionFromPoolTimeout, cancelTimeout: false) - ) - XCTAssertEqual(timeoutAction.connection, .none) - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTPConnectionPool+HTTP2ConnectionsTest.swift b/Tests/AsyncHTTPClientTests/HTTPConnectionPool+HTTP2ConnectionsTest.swift deleted file mode 100644 index dd56a9102..000000000 --- a/Tests/AsyncHTTPClientTests/HTTPConnectionPool+HTTP2ConnectionsTest.swift +++ /dev/null @@ -1,772 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOEmbedded -import XCTest - -@testable import AsyncHTTPClient - -class HTTPConnectionPool_HTTP2ConnectionsTests: XCTestCase { - func testCreatingConnections() { - let elg = EmbeddedEventLoopGroup(loops: 4) - var connections = HTTPConnectionPool.HTTP2Connections(generator: .init(), maximumConnectionUses: nil) - - let el1 = elg.next() - let el2 = elg.next() - - // general purpose connection - XCTAssertFalse(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests) - XCTAssertFalse(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests(for: el1)) - let conn1ID = connections.createNewConnection(on: el1) - XCTAssertTrue(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests) - XCTAssertTrue(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests(for: el1)) - let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1) - let (conn1Index, conn1CreatedContext) = connections.newHTTP2ConnectionEstablished( - conn1, - maxConcurrentStreams: 100 - ) - XCTAssertEqual(conn1CreatedContext.availableStreams, 100) - XCTAssertEqual(conn1CreatedContext.isIdle, true) - XCTAssert(conn1CreatedContext.eventLoop === el1) - let (leasedConn1, leasdConnContext1) = connections.leaseStreams(at: conn1Index, count: 1) - XCTAssertEqual(leasedConn1, conn1) - XCTAssertEqual(leasdConnContext1.wasIdle, true) - - // eventLoop connection - XCTAssertTrue(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests) - XCTAssertFalse(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests(for: el2)) - let conn2ID = connections.createNewConnection(on: el2) - XCTAssertTrue(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests(for: el2)) - let conn2: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn2ID, eventLoop: el2) - let (conn2Index, conn2CreatedContext) = connections.newHTTP2ConnectionEstablished( - conn2, - maxConcurrentStreams: 100 - ) - XCTAssertEqual(conn1CreatedContext.availableStreams, 100) - XCTAssertTrue(conn1CreatedContext.isIdle) - XCTAssert(conn2CreatedContext.eventLoop === el2) - - let (leasedConn2, leasdConnContext2) = connections.leaseStreams(at: conn2Index, count: 1) - XCTAssertEqual(leasedConn2, conn2) - XCTAssertEqual(leasdConnContext2.wasIdle, true) - XCTAssertTrue(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests(for: el2)) - } - - func testCreatingConnectionAndFailing() { - let elg = EmbeddedEventLoopGroup(loops: 4) - var connections = HTTPConnectionPool.HTTP2Connections(generator: .init(), maximumConnectionUses: nil) - - let el1 = elg.next() - let el2 = elg.next() - - // general purpose connection - XCTAssertFalse(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests) - XCTAssertFalse(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests(for: el1)) - let conn1ID = connections.createNewConnection(on: el1) - XCTAssertEqual(conn1ID, 0) - XCTAssertTrue(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests) - XCTAssertTrue(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests(for: el1)) - - // connection failed to start. 1. backoff - let backoff1EL = connections.backoffNextConnectionAttempt(conn1ID) - XCTAssert(backoff1EL === el1) - // backoff done. 2. decide what's next - guard let (conn1FailIndex, conn1FailContext) = connections.failConnection(conn1ID) else { - return XCTFail("Expected that the connection is remembered") - } - - XCTAssert(conn1FailContext.eventLoop === el1) - XCTAssertFalse(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests) - XCTAssertFalse(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests(for: el1)) - let (replaceConn1ID, replaceConn1EL) = connections.createNewConnectionByReplacingClosedConnection( - at: conn1FailIndex - ) - XCTAssert(replaceConn1EL === el1) - XCTAssertEqual(replaceConn1ID, 1) - XCTAssertTrue(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests) - XCTAssertTrue(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests(for: el1)) - - // eventLoop connection - let conn2ID = connections.createNewConnection(on: el2) - XCTAssertTrue(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests) - XCTAssertTrue(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests(for: el2)) - let backoff2EL = connections.backoffNextConnectionAttempt(conn2ID) - XCTAssert(backoff2EL === el2) - guard let (conn2FailIndex, conn2FailContext) = connections.failConnection(conn2ID) else { - return XCTFail("Expected that the connection is remembered") - } - XCTAssert(conn2FailContext.eventLoop === el2) - connections.removeConnection(at: conn2FailIndex) - XCTAssertFalse(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests(for: el2)) - } - - func testFailConnectionRace() { - let elg = EmbeddedEventLoopGroup(loops: 5) - - let el1 = elg.next() - - var connections = HTTPConnectionPool.HTTP2Connections(generator: .init(), maximumConnectionUses: nil) - - // connection is idle - let conn1ID = connections.createNewConnection(on: el1) - let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1) - _ = connections.newHTTP2ConnectionEstablished(conn1, maxConcurrentStreams: 100) - - // connection close is initiated from the pool - XCTAssertEqual(connections.closeConnectionIfIdle(conn1ID), conn1) - - // connection will report close event to us even if we have initialled it an we need to tolerate it - XCTAssertNil(connections.failConnection(conn1ID)) - } - - func testLeaseConnectionOfPreferredButUnavailableEL() { - let elg = EmbeddedEventLoopGroup(loops: 5) - let el1 = elg.next() - let el2 = elg.next() - let el3 = elg.next() - let el4 = elg.next() - let el5 = elg.next() - - var connections = HTTPConnectionPool.HTTP2Connections(generator: .init(), maximumConnectionUses: nil) - XCTAssertFalse(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests) - for el in [el1, el2, el3, el4] { - XCTAssertFalse(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests(for: el)) - let connID = connections.createNewConnection(on: el) - XCTAssertTrue(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests) - XCTAssertTrue(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests(for: el)) - let conn: HTTPConnectionPool.Connection = .__testOnly_connection(id: connID, eventLoop: el) - let (_, conn1CreatedContext) = connections.newHTTP2ConnectionEstablished(conn, maxConcurrentStreams: 100) - XCTAssertEqual(conn1CreatedContext.availableStreams, 100) - XCTAssertEqual(conn1CreatedContext.isIdle, true) - XCTAssert(conn1CreatedContext.eventLoop === el) - } - - XCTAssertNotNil(connections.leaseStream(onPreferred: el5)) - } - - func testLeaseConnectionOnRequiredButUnavailableEL() { - let elg = EmbeddedEventLoopGroup(loops: 5) - let el1 = elg.next() - let el2 = elg.next() - let el3 = elg.next() - let el4 = elg.next() - let el5 = elg.next() - - var connections = HTTPConnectionPool.HTTP2Connections(generator: .init(), maximumConnectionUses: nil) - XCTAssertFalse(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests) - for el in [el1, el2, el3, el4] { - XCTAssertFalse(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests(for: el)) - let connID = connections.createNewConnection(on: el) - XCTAssertTrue(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests) - XCTAssertTrue(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests(for: el)) - let conn: HTTPConnectionPool.Connection = .__testOnly_connection(id: connID, eventLoop: el) - let (_, conn1CreatedContext) = connections.newHTTP2ConnectionEstablished(conn, maxConcurrentStreams: 100) - XCTAssertEqual(conn1CreatedContext.availableStreams, 100) - XCTAssertEqual(conn1CreatedContext.isIdle, true) - XCTAssert(conn1CreatedContext.eventLoop === el) - } - - XCTAssertNil(connections.leaseStream(onRequired: el5)) - } - - func testCloseConnectionIfIdle() { - let elg = EmbeddedEventLoopGroup(loops: 5) - - let el1 = elg.next() - - var connections = HTTPConnectionPool.HTTP2Connections(generator: .init(), maximumConnectionUses: nil) - - // connection is idle - let conn1ID = connections.createNewConnection(on: el1) - let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1) - _ = connections.newHTTP2ConnectionEstablished(conn1, maxConcurrentStreams: 100) - XCTAssertEqual(connections.closeConnectionIfIdle(conn1ID), conn1) - - // connection is not idle - let conn2ID = connections.createNewConnection(on: el1) - let conn2: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn2ID, eventLoop: el1) - let (conn2Index, _) = connections.newHTTP2ConnectionEstablished(conn2, maxConcurrentStreams: 100) - - let (leasedConn1, leasdConnContext1) = connections.leaseStreams(at: conn2Index, count: 1) - XCTAssertEqual(leasedConn1, conn2) - XCTAssertEqual(leasdConnContext1.wasIdle, true) - XCTAssertNil(connections.closeConnectionIfIdle(conn2ID)) - } - - func testCloseConnectionIfIdleButLeasedRaceCondition() { - let elg = EmbeddedEventLoopGroup(loops: 5) - - let el1 = elg.next() - - var connections = HTTPConnectionPool.HTTP2Connections(generator: .init(), maximumConnectionUses: nil) - - // connection is idle - let conn1ID = connections.createNewConnection(on: el1) - let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1) - _ = connections.newHTTP2ConnectionEstablished(conn1, maxConcurrentStreams: 100) - - // connection is leased - guard let (leasedConn, leaseContext) = connections.leaseStream(onPreferred: el1) else { - return XCTFail("lease unexpectedly failed") - } - XCTAssertEqual(leasedConn, conn1) - XCTAssertEqual(leaseContext.wasIdle, true) - - // timeout arrives minimal to late - XCTAssertEqual(connections.closeConnectionIfIdle(conn1ID), nil) - } - - func testCloseConnectionIfIdleButClosedRaceCondition() { - let elg = EmbeddedEventLoopGroup(loops: 5) - - let el1 = elg.next() - - var connections = HTTPConnectionPool.HTTP2Connections(generator: .init(), maximumConnectionUses: nil) - - // connection is idle - let conn1ID = connections.createNewConnection(on: el1) - let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1) - _ = connections.newHTTP2ConnectionEstablished(conn1, maxConcurrentStreams: 100) - _ = connections.failConnection(conn1ID) - - // timeout arrives minimal to late - XCTAssertEqual(connections.closeConnectionIfIdle(conn1ID), nil) - } - - func testCloseConnectionIfIdleRace() { - let elg = EmbeddedEventLoopGroup(loops: 5) - - let el1 = elg.next() - - var connections = HTTPConnectionPool.HTTP2Connections(generator: .init(), maximumConnectionUses: nil) - - // connection is idle - let conn1ID = connections.createNewConnection(on: el1) - let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1) - _ = connections.newHTTP2ConnectionEstablished(conn1, maxConcurrentStreams: 100) - - // we lease it just before timeout - guard let (leasedConn, leaseContext) = connections.leaseStream(onRequired: el1) else { - return XCTFail("lease unexpectedly failed") - } - XCTAssertEqual(leasedConn, conn1) - XCTAssertEqual(leaseContext.wasIdle, true) - - // timeout arrives minimal to late - XCTAssertEqual(connections.closeConnectionIfIdle(conn1ID), nil) - } - - func testShutdown() { - let elg = EmbeddedEventLoopGroup(loops: 6) - let el1 = elg.next() - let el2 = elg.next() - let el3 = elg.next() - let el4 = elg.next() - let el5 = elg.next() - let el6 = elg.next() - - var connections = HTTPConnectionPool.HTTP2Connections(generator: .init(), maximumConnectionUses: nil) - XCTAssertFalse(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests) - for el in [el1, el2, el3, el4] { - XCTAssertFalse(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests(for: el)) - let connID = connections.createNewConnection(on: el) - XCTAssertTrue(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests) - XCTAssertTrue(connections.hasConnectionThatCanOrWillBeAbleToExecuteRequests(for: el)) - let conn: HTTPConnectionPool.Connection = .__testOnly_connection(id: connID, eventLoop: el) - let (_, conn1CreatedContext) = connections.newHTTP2ConnectionEstablished(conn, maxConcurrentStreams: 100) - XCTAssertEqual(conn1CreatedContext.availableStreams, 100) - XCTAssertEqual(conn1CreatedContext.isIdle, true) - XCTAssert(conn1CreatedContext.eventLoop === el) - } - - XCTAssertEqual(connections.stats.backingOffConnections, 0) - XCTAssertEqual(connections.stats.leasedStreams, 0) - XCTAssertEqual(connections.stats.availableStreams, 400) - XCTAssertEqual(connections.stats.idleConnections, 4) - - // connection is leased - guard let (leasedConn, leaseContext) = connections.leaseStream(onPreferred: el1) else { - return XCTFail("Expected to be able to lease a connection") - } - XCTAssertEqual(leasedConn, .__testOnly_connection(id: 0, eventLoop: el1)) - XCTAssertEqual(leaseContext.wasIdle, true) - - XCTAssertEqual(connections.stats.backingOffConnections, 0) - XCTAssertEqual(connections.stats.leasedStreams, 1) - XCTAssertEqual(connections.stats.availableStreams, 399) - XCTAssertEqual(connections.stats.idleConnections, 3) - - // start another connection that fails - let backingOffID = connections.createNewConnection(on: el5) - XCTAssert(connections.backoffNextConnectionAttempt(backingOffID) === el5) - - // start another connection - let startingID = connections.createNewConnection(on: el6) - - let context = connections.shutdown() - XCTAssertEqual(context.close.count, 3) - XCTAssertEqual(context.cancel, [leasedConn]) - XCTAssertEqual(context.connectBackoff, [backingOffID]) - - XCTAssertEqual(connections.stats.idleConnections, 0) - XCTAssertEqual(connections.stats.backingOffConnections, 0) - XCTAssertEqual(connections.stats.leasedStreams, 1) - XCTAssertEqual(connections.stats.availableStreams, 99) - XCTAssertEqual(connections.stats.startingConnections, 1) - XCTAssertFalse(connections.isEmpty) - - let (releaseIndex, _) = connections.releaseStream(leasedConn.id) - XCTAssertEqual(connections.closeConnection(at: releaseIndex), leasedConn) - XCTAssertFalse(connections.isEmpty) - - guard let (failIndex, _) = connections.failConnection(startingID) else { - return XCTFail("Expected that the connection is remembered") - } - connections.removeConnection(at: failIndex) - XCTAssertTrue(connections.isEmpty) - } - - func testLeasingAllConnections() { - let elg = EmbeddedEventLoopGroup(loops: 4) - var connections = HTTPConnectionPool.HTTP2Connections(generator: .init(), maximumConnectionUses: nil) - let el1 = elg.next() - - let conn1ID = connections.createNewConnection(on: el1) - let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1) - let (conn1Index, conn1CreatedContext) = connections.newHTTP2ConnectionEstablished( - conn1, - maxConcurrentStreams: 100 - ) - XCTAssertEqual(conn1CreatedContext.availableStreams, 100) - let (leasedConn1, leasdConnContext1) = connections.leaseStreams(at: conn1Index, count: 100) - XCTAssertEqual(leasedConn1, conn1) - XCTAssertEqual(leasdConnContext1.wasIdle, true) - - XCTAssertNil( - connections.leaseStream(onRequired: el1), - "should not be able to lease stream because they are all already leased" - ) - - let (_, releaseContext) = connections.releaseStream(conn1ID) - XCTAssertFalse(releaseContext.isIdle) - XCTAssertEqual(releaseContext.availableStreams, 1) - - guard let (leasedConn, leaseContext) = connections.leaseStream(onRequired: el1) else { - return XCTFail("lease unexpectedly failed") - } - XCTAssertEqual(leasedConn, conn1) - XCTAssertEqual(leaseContext.wasIdle, false) - - XCTAssertNil( - connections.leaseStream(onRequired: el1), - "should not be able to lease stream because they are all already leased" - ) - } - - func testGoAway() { - let elg = EmbeddedEventLoopGroup(loops: 4) - var connections = HTTPConnectionPool.HTTP2Connections(generator: .init(), maximumConnectionUses: nil) - let el1 = elg.next() - - let conn1ID = connections.createNewConnection(on: el1) - let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1) - let (conn1Index, conn1CreatedContext) = connections.newHTTP2ConnectionEstablished( - conn1, - maxConcurrentStreams: 10 - ) - XCTAssertEqual(conn1CreatedContext.availableStreams, 10) - - let (leasedConn1, leasdConnContext1) = connections.leaseStreams(at: conn1Index, count: 2) - XCTAssertEqual(leasedConn1, conn1) - XCTAssertEqual(leasdConnContext1.wasIdle, true) - - XCTAssertTrue(connections.goAwayReceived(conn1ID)?.eventLoop === el1) - - XCTAssertEqual( - connections.stats, - .init( - startingConnections: 0, - backingOffConnections: 0, - idleConnections: 0, - availableConnections: 0, - drainingConnections: 1, - leasedStreams: 2, - availableStreams: 0 - ) - ) - - XCTAssertNil( - connections.leaseStream(onRequired: el1), - "we should not be able to lease a stream because the connection is draining" - ) - - // a server can potentially send more than one connection go away and we should not crash - XCTAssertTrue(connections.goAwayReceived(conn1ID)?.eventLoop === el1) - XCTAssertEqual( - connections.stats, - .init( - startingConnections: 0, - backingOffConnections: 0, - idleConnections: 0, - availableConnections: 0, - drainingConnections: 1, - leasedStreams: 2, - availableStreams: 0 - ) - ) - - // release a connection - let (_, release1Context) = connections.releaseStream(conn1ID) - XCTAssertFalse(release1Context.isIdle) - XCTAssertEqual(release1Context.availableStreams, 0) - XCTAssertEqual( - connections.stats, - .init( - startingConnections: 0, - backingOffConnections: 0, - idleConnections: 0, - availableConnections: 0, - drainingConnections: 1, - leasedStreams: 1, - availableStreams: 0 - ) - ) - - // release last connection - let (_, release2Context) = connections.releaseStream(conn1ID) - XCTAssertFalse(release2Context.isIdle) - XCTAssertEqual(release2Context.availableStreams, 0) - XCTAssertEqual( - connections.stats, - .init( - startingConnections: 0, - backingOffConnections: 0, - idleConnections: 0, - availableConnections: 0, - drainingConnections: 1, - leasedStreams: 0, - availableStreams: 0 - ) - ) - } - - func testNewMaxConcurrentStreamsSetting() { - let elg = EmbeddedEventLoopGroup(loops: 4) - var connections = HTTPConnectionPool.HTTP2Connections(generator: .init(), maximumConnectionUses: nil) - let el1 = elg.next() - - let conn1ID = connections.createNewConnection(on: el1) - let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1) - let (conn1Index, conn1CreatedContext) = connections.newHTTP2ConnectionEstablished( - conn1, - maxConcurrentStreams: 1 - ) - XCTAssertEqual(conn1CreatedContext.availableStreams, 1) - - let (leasedConn1, leasdConnContext1) = connections.leaseStreams(at: conn1Index, count: 1) - XCTAssertEqual(leasedConn1, conn1) - XCTAssertEqual(leasdConnContext1.wasIdle, true) - - XCTAssertNil(connections.leaseStream(onRequired: el1), "all streams are in use") - - guard let (_, newSettingsContext1) = connections.newHTTP2MaxConcurrentStreamsReceived(conn1ID, newMaxStreams: 2) - else { - return XCTFail("Expected to get a new settings context") - } - XCTAssertEqual(newSettingsContext1.availableStreams, 1) - XCTAssertTrue(newSettingsContext1.eventLoop === el1) - XCTAssertFalse(newSettingsContext1.isIdle) - - guard let (leasedConn2, leaseContext2) = connections.leaseStream(onRequired: el1) else { - return XCTFail("lease unexpectedly failed") - } - XCTAssertEqual(leasedConn2, conn1) - XCTAssertEqual(leaseContext2.wasIdle, false) - - guard let (_, newSettingsContext2) = connections.newHTTP2MaxConcurrentStreamsReceived(conn1ID, newMaxStreams: 1) - else { - return XCTFail("Expected to get a new settings context") - } - XCTAssertEqual(newSettingsContext2.availableStreams, 0) - XCTAssertTrue(newSettingsContext2.eventLoop === el1) - XCTAssertFalse(newSettingsContext2.isIdle) - - // release a connection - let (_, release1Context) = connections.releaseStream(conn1ID) - XCTAssertFalse(release1Context.isIdle) - XCTAssertEqual(release1Context.availableStreams, 0) - - XCTAssertNil(connections.leaseStream(onRequired: el1), "all streams are in use") - - // release a connection - let (_, release2Context) = connections.releaseStream(conn1ID) - XCTAssertTrue(release2Context.isIdle) - XCTAssertEqual(release2Context.availableStreams, 1) - - guard let (leasedConn3, leaseContext3) = connections.leaseStream(onRequired: el1) else { - return XCTFail("lease unexpectedly failed") - } - XCTAssertEqual(leasedConn3, conn1) - XCTAssertEqual(leaseContext3.wasIdle, true) - } - - func testEventsAfterConnectionIsClosed() { - let elg = EmbeddedEventLoopGroup(loops: 2) - var connections = HTTPConnectionPool.HTTP2Connections(generator: .init(), maximumConnectionUses: nil) - let el1 = elg.next() - - let conn1ID = connections.createNewConnection(on: el1) - let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1) - let (conn1Index, conn1CreatedContext) = connections.newHTTP2ConnectionEstablished( - conn1, - maxConcurrentStreams: 1 - ) - XCTAssertEqual(conn1CreatedContext.availableStreams, 1) - - let (leasedConn1, leasdConnContext1) = connections.leaseStreams(at: conn1Index, count: 1) - XCTAssertEqual(leasedConn1, conn1) - XCTAssertEqual(leasdConnContext1.wasIdle, true) - - XCTAssertNil(connections.leaseStream(onRequired: el1), "all streams are in use") - - let (_, releaseContext) = connections.releaseStream(conn1ID) - XCTAssertTrue(releaseContext.eventLoop === el1) - XCTAssertEqual(releaseContext.availableStreams, 1) - XCTAssertEqual(releaseContext.connectionID, conn1ID) - XCTAssertEqual(releaseContext.isIdle, true) - - // schedule timeout... this should remove the connection from http2Connections - - XCTAssertEqual(connections.closeConnectionIfIdle(conn1ID), conn1) - - // events race with the complete shutdown - - XCTAssertNil(connections.newHTTP2MaxConcurrentStreamsReceived(conn1ID, newMaxStreams: 2)) - XCTAssertNil(connections.goAwayReceived(conn1ID)) - - // finally close event - XCTAssertNil(connections.failConnection(conn1ID)) - } - - func testLeaseOnPreferredEventLoopWithoutAnyAvailable() { - let elg = EmbeddedEventLoopGroup(loops: 4) - var connections = HTTPConnectionPool.HTTP2Connections(generator: .init(), maximumConnectionUses: nil) - let el1 = elg.next() - - let conn1ID = connections.createNewConnection(on: el1) - let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1) - let (conn1Index, conn1CreatedContext) = connections.newHTTP2ConnectionEstablished( - conn1, - maxConcurrentStreams: 1 - ) - XCTAssertEqual(conn1CreatedContext.availableStreams, 1) - let (leasedConn1, leasdConnContext1) = connections.leaseStreams(at: conn1Index, count: 1) - XCTAssertEqual(leasedConn1, conn1) - XCTAssertEqual(leasdConnContext1.wasIdle, true) - - XCTAssertNil(connections.leaseStream(onPreferred: el1), "all streams are in use") - } - - func testMigrationFromHTTP1() { - let elg = EmbeddedEventLoopGroup(loops: 4) - var connections = HTTPConnectionPool.HTTP2Connections(generator: .init(), maximumConnectionUses: nil) - let el1 = elg.next() - let el2 = elg.next() - let conn1ID: HTTPConnectionPool.Connection.ID = 1 - let conn2ID: HTTPConnectionPool.Connection.ID = 2 - - connections.migrateFromHTTP1( - starting: [(conn1ID, el1)], - backingOff: [(conn2ID, el2)] - ) - XCTAssertTrue( - connections.createConnectionsAfterMigrationIfNeeded( - requiredEventLoopsOfPendingRequests: [el1, el2] - ).isEmpty - ) - - XCTAssertEqual( - connections.stats, - .init( - startingConnections: 1, - backingOffConnections: 1, - idleConnections: 0, - availableConnections: 0, - drainingConnections: 0, - leasedStreams: 0, - availableStreams: 0 - ) - ) - - let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1) - let (conn1Index, conn1CreatedContext) = connections.newHTTP2ConnectionEstablished( - conn1, - maxConcurrentStreams: 100 - ) - XCTAssertEqual(conn1CreatedContext.availableStreams, 100) - - let (leasedConn1, leasdConnContext1) = connections.leaseStreams(at: conn1Index, count: 2) - XCTAssertEqual(leasedConn1, conn1) - XCTAssertEqual(leasdConnContext1.wasIdle, true) - - XCTAssertEqual( - connections.stats, - .init( - startingConnections: 0, - backingOffConnections: 1, - idleConnections: 0, - availableConnections: 1, - drainingConnections: 0, - leasedStreams: 2, - availableStreams: 98 - ) - ) - } - - func testMigrationToHTTP1() { - let elg = EmbeddedEventLoopGroup(loops: 4) - let generator = HTTPConnectionPool.Connection.ID.Generator() - var connections = HTTPConnectionPool.HTTP2Connections(generator: generator, maximumConnectionUses: nil) - let el1 = elg.next() - let el2 = elg.next() - let el3 = elg.next() - let el4 = elg.next() - - let conn1ID = generator.next() - let conn2ID = generator.next() - let conn3ID = generator.next() - let conn4ID = generator.next() - - connections.migrateFromHTTP1( - starting: [(conn1ID, el1), (conn2ID, el2), (conn3ID, el3)], - backingOff: [(conn4ID, el4)] - ) - - let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1) - let (conn1Index, conn1CreatedContext) = connections.newHTTP2ConnectionEstablished( - conn1, - maxConcurrentStreams: 100 - ) - XCTAssertEqual(conn1CreatedContext.availableStreams, 100) - - let (leasedConn1, leasdConnContext1) = connections.leaseStreams(at: conn1Index, count: 2) - XCTAssertEqual(leasedConn1, conn1) - XCTAssertEqual(leasdConnContext1.wasIdle, true) - - let conn2: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn2ID, eventLoop: el2) - let (_, conn2CreatedContext) = connections.newHTTP2ConnectionEstablished(conn2, maxConcurrentStreams: 100) - XCTAssertEqual(conn2CreatedContext.availableStreams, 100) - - XCTAssertEqual( - connections.stats, - .init( - startingConnections: 1, - backingOffConnections: 1, - idleConnections: 1, - availableConnections: 2, - drainingConnections: 0, - leasedStreams: 2, - availableStreams: 198 - ) - ) - - let migrationContext = connections.migrateToHTTP1() - XCTAssertEqual(migrationContext.close, [conn2]) - XCTAssertEqual(migrationContext.starting.map { $0.0 }, [conn3ID]) - XCTAssertEqual(migrationContext.starting.map { $0.1.id }, [el3.id]) - XCTAssertEqual(migrationContext.backingOff.map { $0.0 }, [conn4ID]) - XCTAssertEqual(migrationContext.backingOff.map { $0.1.id }, [el4.id]) - - XCTAssertEqual( - connections.stats, - .init( - startingConnections: 0, - backingOffConnections: 0, - idleConnections: 0, - availableConnections: 1, - drainingConnections: 0, - leasedStreams: 2, - availableStreams: 98 - ) - ) - } - - func testMigrationFromHTTP1WithPendingRequestsWithRequiredEventLoop() { - let elg = EmbeddedEventLoopGroup(loops: 4) - let generator = HTTPConnectionPool.Connection.ID.Generator() - var connections = HTTPConnectionPool.HTTP2Connections(generator: generator, maximumConnectionUses: nil) - let el1 = elg.next() - let el2 = elg.next() - let el3 = elg.next() - let conn1ID = generator.next() - let conn2ID = generator.next() - - connections.migrateFromHTTP1( - starting: [(conn1ID, el1)], - backingOff: [(conn2ID, el2)] - ) - let newConnections = connections.createConnectionsAfterMigrationIfNeeded( - requiredEventLoopsOfPendingRequests: [el1, el2, el3] - ) - - XCTAssertEqual(newConnections.count, 1) - - guard let (conn3ID, eventLoop) = newConnections.first else { - return XCTFail("expected to start a new connection") - } - XCTAssertTrue(eventLoop === el3) - - let conn3: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn3ID, eventLoop: el3) - let (_, context) = connections.newHTTP2ConnectionEstablished(conn3, maxConcurrentStreams: 100) - XCTAssertEqual(context.availableStreams, 100) - XCTAssertEqual(context.eventLoop.id, el3.id) - XCTAssertEqual(context.isIdle, true) - XCTAssertEqual(context.connectionID, conn3ID) - } - - func testMigrationFromHTTP1WithAlreadyEstablishedHTTP2Connection() { - let elg = EmbeddedEventLoopGroup(loops: 4) - let generator = HTTPConnectionPool.Connection.ID.Generator() - var connections = HTTPConnectionPool.HTTP2Connections(generator: generator, maximumConnectionUses: nil) - let el1 = elg.next() - let el2 = elg.next() - let el3 = elg.next() - - let conn1ID = connections.createNewConnection(on: el1) - let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1) - let (index, _) = connections.newHTTP2ConnectionEstablished(conn1, maxConcurrentStreams: 100) - _ = connections.leaseStreams(at: index, count: 1) - - let conn2ID = generator.next() - let conn3ID = generator.next() - - connections.migrateFromHTTP1( - starting: [(conn2ID, el2)], - backingOff: [(conn3ID, el3)] - ) - - XCTAssertTrue( - connections.createConnectionsAfterMigrationIfNeeded( - requiredEventLoopsOfPendingRequests: [el1, el2, el3] - ).isEmpty, - "we still have an active connection for el1 and should not create a new one" - ) - - guard let (leasedConn, _) = connections.leaseStream(onRequired: el1) else { - return XCTFail("could not lease stream on el1") - } - XCTAssertEqual(leasedConn, conn1) - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTPConnectionPool+HTTP2StateMachineTests.swift b/Tests/AsyncHTTPClientTests/HTTPConnectionPool+HTTP2StateMachineTests.swift deleted file mode 100644 index e64fd5e71..000000000 --- a/Tests/AsyncHTTPClientTests/HTTPConnectionPool+HTTP2StateMachineTests.swift +++ /dev/null @@ -1,1544 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOEmbedded -import NIOHTTP1 -import NIOPosix -import XCTest - -@testable import AsyncHTTPClient - -private typealias Action = HTTPConnectionPool.StateMachine.Action -private typealias ConnectionAction = HTTPConnectionPool.StateMachine.ConnectionAction -private typealias RequestAction = HTTPConnectionPool.StateMachine.RequestAction - -class HTTPConnectionPool_HTTP2StateMachineTests: XCTestCase { - func testCreatingOfConnection() { - let elg = EmbeddedEventLoopGroup(loops: 1) - let el1 = elg.next() - var connections = MockConnectionPool() - var queuer = MockRequestQueuer() - var state = HTTPConnectionPool.HTTP2StateMachine( - idGenerator: .init(), - retryConnectionEstablishment: true, - lifecycleState: .running, - maximumConnectionUses: nil - ) - - /// first request should create a new connection - let mockRequest = MockHTTPScheduableRequest(eventLoop: el1) - let request = HTTPConnectionPool.Request(mockRequest) - let executeAction = state.executeRequest(request) - - guard case .createConnection(let connID, let eventLoop) = executeAction.connection else { - return XCTFail("Unexpected connection action \(executeAction.connection)") - } - XCTAssertTrue(eventLoop === el1) - - XCTAssertEqual(executeAction.request, .scheduleRequestTimeout(for: request, on: mockRequest.eventLoop)) - - XCTAssertNoThrow(try connections.createConnection(connID, on: el1)) - XCTAssertNoThrow(try queuer.queue(mockRequest, id: request.id)) - - /// subsequent requests should not create a connection - for _ in 0..<9 { - let mockRequest = MockHTTPScheduableRequest(eventLoop: el1) - let request = HTTPConnectionPool.Request(mockRequest) - let action = state.executeRequest(request) - - XCTAssertEqual(action.connection, .none) - XCTAssertEqual(action.request, .scheduleRequestTimeout(for: request, on: mockRequest.eventLoop)) - - XCTAssertNoThrow(try queuer.queue(mockRequest, id: request.id)) - } - - /// connection establishment should result in 5 request executions because we set max concurrent streams to 5 - var maybeConn: HTTPConnectionPool.Connection? - XCTAssertNoThrow(maybeConn = try connections.succeedConnectionCreationHTTP2(connID, maxConcurrentStreams: 5)) - guard let conn = maybeConn else { - return XCTFail("unexpected throw") - } - let action = state.newHTTP2ConnectionEstablished(conn, maxConcurrentStreams: 5) - - XCTAssertEqual(action.connection, .none) - guard case .executeRequestsAndCancelTimeouts(let requests, conn) = action.request else { - return XCTFail("Unexpected request action \(action.request)") - } - XCTAssertEqual(requests.count, 5) - - for request in requests { - XCTAssertNoThrow(try queuer.get(request.id, request: request.__testOnly_wrapped_request())) - } - - /// closing a stream while we have requests queued should result in one request execution action - for _ in 0..<5 { - let action = state.http2ConnectionStreamClosed(connID) - XCTAssertEqual(action.connection, .none) - guard case .executeRequestsAndCancelTimeouts(let requests, conn) = action.request else { - return XCTFail("Unexpected request action \(action.request)") - } - XCTAssertEqual(requests.count, 1) - for request in requests { - XCTAssertNoThrow(try queuer.cancel(request.id)) - } - } - XCTAssertTrue(queuer.isEmpty) - - /// closing streams without any queued requests shouldn't do anything if it's *not* the last stream - for _ in 0..<4 { - let action = state.http2ConnectionStreamClosed(connID) - XCTAssertEqual(action.request, .none) - XCTAssertEqual(action.connection, .none) - } - - /// 4 streams are available and therefore request should be executed immediately - for _ in 0..<4 { - let mockRequest = MockHTTPScheduableRequest(eventLoop: el1, requiresEventLoopForChannel: true) - let request = HTTPConnectionPool.Request(mockRequest) - let action = state.executeRequest(request) - - XCTAssertEqual(action.connection, .none) - XCTAssertEqual(action.request, .executeRequest(request, conn, cancelTimeout: false)) - } - - /// closing streams without any queued requests shouldn't do anything if it's *not* the last stream - for _ in 0..<4 { - let action = state.http2ConnectionStreamClosed(connID) - XCTAssertEqual(action.request, .none) - XCTAssertEqual(action.connection, .none) - } - - /// closing the last stream should schedule a idle timeout - let streamCloseAction = state.http2ConnectionStreamClosed(connID) - XCTAssertEqual(streamCloseAction.request, .none) - XCTAssertEqual(streamCloseAction.connection, .scheduleTimeoutTimer(connID, on: el1)) - - /// shutdown should only close one connection - let shutdownAction = state.shutdown() - XCTAssertEqual(shutdownAction.request, .none) - XCTAssertEqual( - shutdownAction.connection, - .cleanupConnections( - .init( - close: [conn], - cancel: [], - connectBackoff: [] - ), - isShutdown: .yes(unclean: false) - ) - ) - } - - func testConnectionFailureBackoff() { - let elg = EmbeddedEventLoopGroup(loops: 4) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - - var state = HTTPConnectionPool.HTTP2StateMachine( - idGenerator: .init(), - retryConnectionEstablishment: true, - lifecycleState: .running, - maximumConnectionUses: nil - ) - - let mockRequest = MockHTTPScheduableRequest(eventLoop: elg.next()) - let request = HTTPConnectionPool.Request(mockRequest) - - let action = state.executeRequest(request) - XCTAssertEqual(.scheduleRequestTimeout(for: request, on: mockRequest.eventLoop), action.request) - - // 1. connection attempt - guard case .createConnection(let connectionID, on: let connectionEL) = action.connection else { - return XCTFail("Unexpected connection action: \(action.connection)") - } - XCTAssert(connectionEL === mockRequest.eventLoop) // XCTAssertIdentical not available on Linux - - let failedConnect1 = state.failedToCreateNewConnection( - HTTPClientError.connectTimeout, - connectionID: connectionID - ) - XCTAssertEqual(failedConnect1.request, .none) - guard case .scheduleBackoffTimer(connectionID, let backoffTimeAmount1, _) = failedConnect1.connection else { - return XCTFail("Unexpected connection action: \(failedConnect1.connection)") - } - - // 2. connection attempt - let backoffDoneAction = state.connectionCreationBackoffDone(connectionID) - XCTAssertEqual(backoffDoneAction.request, .none) - guard case .createConnection(let newConnectionID, on: let newEventLoop) = backoffDoneAction.connection else { - return XCTFail("Unexpected connection action: \(backoffDoneAction.connection)") - } - XCTAssertGreaterThan(newConnectionID, connectionID) - XCTAssert(connectionEL === newEventLoop) // XCTAssertIdentical not available on Linux - - let failedConnect2 = state.failedToCreateNewConnection( - HTTPClientError.connectTimeout, - connectionID: newConnectionID - ) - XCTAssertEqual(failedConnect2.request, .none) - guard case .scheduleBackoffTimer(newConnectionID, let backoffTimeAmount2, _) = failedConnect2.connection else { - return XCTFail("Unexpected connection action: \(failedConnect2.connection)") - } - - XCTAssertNotEqual(backoffTimeAmount2, backoffTimeAmount1) - - // 3. request times out - let failRequest = state.timeoutRequest(request.id) - guard case .failRequest(let requestToFail, let requestError, cancelTimeout: false) = failRequest.request else { - return XCTFail("Unexpected request action: \(action.request)") - } - // XCTAssertIdentical not available on Linux - XCTAssert(requestToFail.__testOnly_wrapped_request() === mockRequest) - XCTAssertEqual(requestError as? HTTPClientError, .connectTimeout) - XCTAssertEqual(failRequest.connection, .none) - - // 4. retry connection, but no more queued requests. - XCTAssertEqual(state.connectionCreationBackoffDone(newConnectionID), .none) - } - - func testConnectionFailureWhileShuttingDown() { - struct SomeError: Error, Equatable {} - let elg = EmbeddedEventLoopGroup(loops: 4) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - - var state = HTTPConnectionPool.HTTP2StateMachine( - idGenerator: .init(), - retryConnectionEstablishment: false, - lifecycleState: .running, - maximumConnectionUses: nil - ) - - let mockRequest = MockHTTPScheduableRequest(eventLoop: elg.next()) - let request = HTTPConnectionPool.Request(mockRequest) - - let action = state.executeRequest(request) - XCTAssertEqual(.scheduleRequestTimeout(for: request, on: mockRequest.eventLoop), action.request) - - // 1. connection attempt - guard case .createConnection(let connectionID, on: let connectionEL) = action.connection else { - return XCTFail("Unexpected connection action: \(action.connection)") - } - XCTAssert(connectionEL === mockRequest.eventLoop) // XCTAssertIdentical not available on Linux - - // 2. initialise shutdown - let shutdownAction = state.shutdown() - XCTAssertEqual(shutdownAction.connection, .cleanupConnections(.init(), isShutdown: .no)) - guard case .failRequestsAndCancelTimeouts(let requestsToFail, let requestError) = shutdownAction.request else { - return XCTFail("Unexpected request action: \(action.request)") - } - XCTAssertEqualTypeAndValue(requestError, HTTPClientError.cancelled) - XCTAssertEqualTypeAndValue(requestsToFail, [request]) - - // 3. connection attempt fails - let failedConnectAction = state.failedToCreateNewConnection(SomeError(), connectionID: connectionID) - XCTAssertEqual(failedConnectAction.request, .none) - XCTAssertEqual(failedConnectAction.connection, .cleanupConnections(.init(), isShutdown: .yes(unclean: true))) - } - - func testConnectionFailureWithoutRetry() { - struct SomeError: Error, Equatable {} - let elg = EmbeddedEventLoopGroup(loops: 4) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - - var state = HTTPConnectionPool.HTTP2StateMachine( - idGenerator: .init(), - retryConnectionEstablishment: false, - lifecycleState: .running, - maximumConnectionUses: nil - ) - - let mockRequest = MockHTTPScheduableRequest(eventLoop: elg.next()) - let request = HTTPConnectionPool.Request(mockRequest) - - let action = state.executeRequest(request) - XCTAssertEqual(.scheduleRequestTimeout(for: request, on: mockRequest.eventLoop), action.request) - - // 1. connection attempt - guard case .createConnection(let connectionID, on: let connectionEL) = action.connection else { - return XCTFail("Unexpected connection action: \(action.connection)") - } - XCTAssert(connectionEL === mockRequest.eventLoop) // XCTAssertIdentical not available on Linux - - let failedConnectAction = state.failedToCreateNewConnection(SomeError(), connectionID: connectionID) - XCTAssertEqual(failedConnectAction.connection, .none) - guard case .failRequestsAndCancelTimeouts(let requestsToFail, let requestError) = failedConnectAction.request - else { - return XCTFail("Unexpected request action: \(action.request)") - } - XCTAssertEqualTypeAndValue(requestError, SomeError()) - XCTAssertEqualTypeAndValue(requestsToFail, [request]) - } - - func testCancelRequestWorks() { - let elg = EmbeddedEventLoopGroup(loops: 4) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - - var state = HTTPConnectionPool.HTTP2StateMachine( - idGenerator: .init(), - retryConnectionEstablishment: true, - lifecycleState: .running, - maximumConnectionUses: nil - ) - - let mockRequest = MockHTTPScheduableRequest(eventLoop: elg.next()) - let request = HTTPConnectionPool.Request(mockRequest) - - let executeAction = state.executeRequest(request) - XCTAssertEqual(.scheduleRequestTimeout(for: request, on: mockRequest.eventLoop), executeAction.request) - - // 1. connection attempt - guard case .createConnection(let connectionID, on: let connectionEL) = executeAction.connection else { - return XCTFail("Unexpected connection action: \(executeAction.connection)") - } - XCTAssert(connectionEL === mockRequest.eventLoop) // XCTAssertIdentical not available on Linux - - // 2. cancel request - let cancelAction = state.cancelRequest(request.id) - XCTAssertEqual(cancelAction.request, .failRequest(request, HTTPClientError.cancelled, cancelTimeout: true)) - XCTAssertEqual(cancelAction.connection, .none) - - // 3. request timeout triggers to late - XCTAssertEqual(state.timeoutRequest(request.id), .none, "To late timeout is ignored") - - // 4. succeed connection attempt - let connectedAction = state.newHTTP2ConnectionEstablished( - .__testOnly_connection(id: connectionID, eventLoop: connectionEL), - maxConcurrentStreams: 100 - ) - XCTAssertEqual(connectedAction.request, .none, "Request must not be executed") - XCTAssertEqual(connectedAction.connection, .scheduleTimeoutTimer(connectionID, on: connectionEL)) - } - - func testExecuteOnShuttingDownPool() { - let elg = EmbeddedEventLoopGroup(loops: 4) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - - var state = HTTPConnectionPool.HTTP2StateMachine( - idGenerator: .init(), - retryConnectionEstablishment: true, - lifecycleState: .running, - maximumConnectionUses: nil - ) - - let mockRequest = MockHTTPScheduableRequest(eventLoop: elg.next()) - let request = HTTPConnectionPool.Request(mockRequest) - - let executeAction = state.executeRequest(request) - XCTAssertEqual(.scheduleRequestTimeout(for: request, on: mockRequest.eventLoop), executeAction.request) - - // 1. connection attempt - guard case .createConnection(let connectionID, on: let connectionEL) = executeAction.connection else { - return XCTFail("Unexpected connection action: \(executeAction.connection)") - } - XCTAssert(connectionEL === mockRequest.eventLoop) // XCTAssertIdentical not available on Linux - - // 2. connection succeeds - let connection: HTTPConnectionPool.Connection = .__testOnly_connection( - id: connectionID, - eventLoop: connectionEL - ) - let connectedAction = state.newHTTP2ConnectionEstablished(connection, maxConcurrentStreams: 100) - guard case .executeRequestsAndCancelTimeouts([request], connection) = connectedAction.request else { - return XCTFail("Unexpected request action: \(connectedAction.request)") - } - XCTAssert(request.__testOnly_wrapped_request() === mockRequest) // XCTAssertIdentical not available on Linux - XCTAssertEqual(connectedAction.connection, .none) - - // 3. shutdown - let shutdownAction = state.shutdown() - XCTAssertEqual(.none, shutdownAction.request) - guard case .cleanupConnections(let cleanupContext, isShutdown: .no) = shutdownAction.connection else { - return XCTFail("Unexpected connection action: \(shutdownAction.connection)") - } - - XCTAssertEqual(cleanupContext.cancel.count, 1) - XCTAssertEqual(cleanupContext.cancel.first?.id, connectionID) - XCTAssertEqual(cleanupContext.close, []) - XCTAssertEqual(cleanupContext.connectBackoff, []) - - // 4. execute another request - let finalMockRequest = MockHTTPScheduableRequest(eventLoop: elg.next()) - let finalRequest = HTTPConnectionPool.Request(finalMockRequest) - let failAction = state.executeRequest(finalRequest) - XCTAssertEqual(failAction.connection, .none) - XCTAssertEqual( - failAction.request, - .failRequest(finalRequest, HTTPClientError.alreadyShutdown, cancelTimeout: false) - ) - - // 5. close open connection - let closeAction = state.http2ConnectionClosed(connectionID) - XCTAssertEqual(closeAction.connection, .cleanupConnections(.init(), isShutdown: .yes(unclean: true))) - XCTAssertEqual(closeAction.request, .none) - } - - func testHTTP1ToHTTP2MigrationAndShutdownIfFirstConnectionIsHTTP1() { - let elg = EmbeddedEventLoopGroup(loops: 4) - let el1 = elg.next() - - let idGenerator = HTTPConnectionPool.Connection.ID.Generator() - var http1State = HTTPConnectionPool.HTTP1StateMachine( - idGenerator: idGenerator, - maximumConcurrentConnections: 8, - retryConnectionEstablishment: true, - maximumConnectionUses: nil, - lifecycleState: .running - ) - - let mockRequest1 = MockHTTPScheduableRequest(eventLoop: el1) - let request1 = HTTPConnectionPool.Request(mockRequest1) - let mockRequest2 = MockHTTPScheduableRequest(eventLoop: el1) - let request2 = HTTPConnectionPool.Request(mockRequest2) - - let executeAction1 = http1State.executeRequest(request1) - XCTAssertEqual(executeAction1.request, .scheduleRequestTimeout(for: request1, on: el1)) - guard case .createConnection(let conn1ID, _) = executeAction1.connection else { - return XCTFail("unexpected connection action \(executeAction1.connection)") - } - let executeAction2 = http1State.executeRequest(request2) - XCTAssertEqual(executeAction2.request, .scheduleRequestTimeout(for: request2, on: el1)) - guard case .createConnection(let conn2ID, _) = executeAction2.connection else { - return XCTFail("unexpected connection action \(executeAction2.connection)") - } - - // first connection is a HTTP1 connection - let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1) - let conn1Action = http1State.newHTTP1ConnectionEstablished(conn1) - XCTAssertEqual(conn1Action.connection, .none) - XCTAssertEqual(conn1Action.request, .executeRequest(request1, conn1, cancelTimeout: true)) - - // second connection is a HTTP2 connection and we need to migrate - let conn2: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn2ID, eventLoop: el1) - var http2State = HTTPConnectionPool.HTTP2StateMachine( - idGenerator: idGenerator, - retryConnectionEstablishment: true, - lifecycleState: .running, - maximumConnectionUses: nil - ) - - let http2ConnectAction = http2State.migrateFromHTTP1( - http1Connections: http1State.connections, - http2Connections: http1State.http2Connections, - requests: http1State.requests, - newHTTP2Connection: conn2, - maxConcurrentStreams: 100 - ) - XCTAssertEqual( - http2ConnectAction.connection, - .migration(createConnections: [], closeConnections: [], scheduleTimeout: nil) - ) - guard case .executeRequestsAndCancelTimeouts([request2], conn2) = http2ConnectAction.request else { - return XCTFail("Unexpected request action \(http2ConnectAction.request)") - } - - // second request is done first - let closeAction = http2State.http2ConnectionStreamClosed(conn2ID) - XCTAssertEqual(closeAction.request, .none) - XCTAssertEqual(closeAction.connection, .scheduleTimeoutTimer(conn2ID, on: el1)) - - let shutdownAction = http2State.shutdown() - XCTAssertEqual(shutdownAction.request, .none) - XCTAssertEqual( - shutdownAction.connection, - .cleanupConnections( - .init( - close: [conn2], - cancel: [], - connectBackoff: [] - ), - isShutdown: .no - ) - ) - - let releaseAction = http2State.http1ConnectionReleased(conn1ID) - XCTAssertEqual(releaseAction.request, .none) - XCTAssertEqual(releaseAction.connection, .closeConnection(conn1, isShutdown: .yes(unclean: true))) - } - - func testSchedulingAndCancelingOfIdleTimeout() { - let elg = EmbeddedEventLoopGroup(loops: 1) - let el1 = elg.next() - - // establish one idle http2 connection - let idGenerator = HTTPConnectionPool.Connection.ID.Generator() - var http1Conns = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 8, - generator: idGenerator, - maximumConnectionUses: nil - ) - let conn1ID = http1Conns.createNewConnection(on: el1) - var state = HTTPConnectionPool.HTTP2StateMachine( - idGenerator: idGenerator, - retryConnectionEstablishment: true, - lifecycleState: .running, - maximumConnectionUses: nil - ) - - let conn1 = HTTPConnectionPool.Connection.__testOnly_connection(id: conn1ID, eventLoop: el1) - let connectAction = state.migrateFromHTTP1( - http1Connections: http1Conns, - requests: .init(), - newHTTP2Connection: conn1, - maxConcurrentStreams: 100 - ) - - XCTAssertEqual(connectAction.request, .none) - XCTAssertEqual( - connectAction.connection, - .migration( - createConnections: [], - closeConnections: [], - scheduleTimeout: (conn1ID, el1) - ) - ) - - // execute request on idle connection - let mockRequest1 = MockHTTPScheduableRequest(eventLoop: el1) - let request1 = HTTPConnectionPool.Request(mockRequest1) - let request1Action = state.executeRequest(request1) - XCTAssertEqual(request1Action.request, .executeRequest(request1, conn1, cancelTimeout: false)) - XCTAssertEqual(request1Action.connection, .cancelTimeoutTimer(conn1ID)) - - // close stream - let closeStream1Action = state.http2ConnectionStreamClosed(conn1ID) - XCTAssertEqual(closeStream1Action.request, .none) - XCTAssertEqual(closeStream1Action.connection, .scheduleTimeoutTimer(conn1ID, on: el1)) - - // execute request on idle connection with required event loop - let mockRequest2 = MockHTTPScheduableRequest(eventLoop: el1, requiresEventLoopForChannel: true) - let request2 = HTTPConnectionPool.Request(mockRequest2) - let request2Action = state.executeRequest(request2) - XCTAssertEqual(request2Action.request, .executeRequest(request2, conn1, cancelTimeout: false)) - XCTAssertEqual(request2Action.connection, .cancelTimeoutTimer(conn1ID)) - - // close stream - let closeStream2Action = state.http2ConnectionStreamClosed(conn1ID) - XCTAssertEqual(closeStream2Action.request, .none) - XCTAssertEqual(closeStream2Action.connection, .scheduleTimeoutTimer(conn1ID, on: el1)) - } - - func testConnectionTimeout() { - let elg = EmbeddedEventLoopGroup(loops: 1) - let el1 = elg.next() - - // establish one idle http2 connection - let idGenerator = HTTPConnectionPool.Connection.ID.Generator() - var http1Conns = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 8, - generator: idGenerator, - maximumConnectionUses: nil - ) - let conn1ID = http1Conns.createNewConnection(on: el1) - var state = HTTPConnectionPool.HTTP2StateMachine( - idGenerator: idGenerator, - retryConnectionEstablishment: true, - lifecycleState: .running, - maximumConnectionUses: nil - ) - - let conn1 = HTTPConnectionPool.Connection.__testOnly_connection(id: conn1ID, eventLoop: el1) - let connectAction = state.migrateFromHTTP1( - http1Connections: http1Conns, - requests: .init(), - newHTTP2Connection: conn1, - maxConcurrentStreams: 100 - ) - XCTAssertEqual(connectAction.request, .none) - XCTAssertEqual( - connectAction.connection, - .migration( - createConnections: [], - closeConnections: [], - scheduleTimeout: (conn1ID, el1) - ) - ) - - // let the connection timeout - let timeoutAction = state.connectionIdleTimeout(conn1ID) - XCTAssertEqual(timeoutAction.request, .none) - XCTAssertEqual(timeoutAction.connection, .closeConnection(conn1, isShutdown: .no)) - } - - func testConnectionEstablishmentFailure() { - struct SomeError: Error, Equatable {} - - let elg = EmbeddedEventLoopGroup(loops: 2) - let el1 = elg.next() - let el2 = elg.next() - - // establish one idle http2 connection - let idGenerator = HTTPConnectionPool.Connection.ID.Generator() - var http1Conns = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 8, - generator: idGenerator, - maximumConnectionUses: nil - ) - let conn1ID = http1Conns.createNewConnection(on: el1) - var state = HTTPConnectionPool.HTTP2StateMachine( - idGenerator: idGenerator, - retryConnectionEstablishment: true, - lifecycleState: .running, - maximumConnectionUses: nil - ) - let conn1 = HTTPConnectionPool.Connection.__testOnly_connection(id: conn1ID, eventLoop: el1) - let connectAction = state.migrateFromHTTP1( - http1Connections: http1Conns, - requests: .init(), - newHTTP2Connection: conn1, - maxConcurrentStreams: 100 - ) - XCTAssertEqual(connectAction.request, .none) - XCTAssertEqual( - connectAction.connection, - .migration( - createConnections: [], - closeConnections: [], - scheduleTimeout: (conn1ID, el1) - ) - ) - - // create new http2 connection - let mockRequest1 = MockHTTPScheduableRequest(eventLoop: el2, requiresEventLoopForChannel: true) - let request1 = HTTPConnectionPool.Request(mockRequest1) - let executeAction = state.executeRequest(request1) - XCTAssertEqual(executeAction.request, .scheduleRequestTimeout(for: request1, on: el2)) - guard case .createConnection(let conn2ID, _) = executeAction.connection else { - return XCTFail("unexpected connection action \(executeAction.connection)") - } - - let action = state.failedToCreateNewConnection(SomeError(), connectionID: conn2ID) - XCTAssertEqual(action.request, .none) - guard case .scheduleBackoffTimer(conn2ID, _, let eventLoop) = action.connection else { - return XCTFail("unexpected connection action \(action.connection)") - } - XCTAssertEqual(eventLoop.id, el2.id) - } - - func testGoAwayOnIdleConnection() { - let elg = EmbeddedEventLoopGroup(loops: 1) - let el1 = elg.next() - - // establish one idle http2 connection - let idGenerator = HTTPConnectionPool.Connection.ID.Generator() - var http1Conns = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 8, - generator: idGenerator, - maximumConnectionUses: nil - ) - let conn1ID = http1Conns.createNewConnection(on: el1) - var state = HTTPConnectionPool.HTTP2StateMachine( - idGenerator: idGenerator, - retryConnectionEstablishment: true, - lifecycleState: .running, - maximumConnectionUses: nil - ) - - let conn1 = HTTPConnectionPool.Connection.__testOnly_connection(id: conn1ID, eventLoop: el1) - - let connectAction = state.migrateFromHTTP1( - http1Connections: http1Conns, - requests: .init(), - newHTTP2Connection: conn1, - maxConcurrentStreams: 100 - ) - XCTAssertEqual(connectAction.request, .none) - XCTAssertEqual( - connectAction.connection, - .migration( - createConnections: [], - closeConnections: [], - scheduleTimeout: (conn1ID, el1) - ) - ) - - let goAwayAction = state.http2ConnectionGoAwayReceived(conn1ID) - XCTAssertEqual(goAwayAction.request, .none) - XCTAssertEqual(goAwayAction.connection, .none, "Connection is automatically closed by HTTP2IdleHandler") - } - - func testGoAwayWithLeasedStream() { - let elg = EmbeddedEventLoopGroup(loops: 1) - let el1 = elg.next() - - // establish one idle http2 connection - let idGenerator = HTTPConnectionPool.Connection.ID.Generator() - var http1Conns = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 8, - generator: idGenerator, - maximumConnectionUses: nil - ) - let conn1ID = http1Conns.createNewConnection(on: el1) - var state = HTTPConnectionPool.HTTP2StateMachine( - idGenerator: idGenerator, - retryConnectionEstablishment: true, - lifecycleState: .running, - maximumConnectionUses: nil - ) - - let conn1 = HTTPConnectionPool.Connection.__testOnly_connection(id: conn1ID, eventLoop: el1) - let connectAction = state.migrateFromHTTP1( - http1Connections: http1Conns, - requests: .init(), - newHTTP2Connection: conn1, - maxConcurrentStreams: 100 - ) - XCTAssertEqual(connectAction.request, .none) - XCTAssertEqual( - connectAction.connection, - .migration( - createConnections: [], - closeConnections: [], - scheduleTimeout: (conn1ID, el1) - ) - ) - - // execute request on idle connection - let mockRequest1 = MockHTTPScheduableRequest(eventLoop: el1) - let request1 = HTTPConnectionPool.Request(mockRequest1) - let request1Action = state.executeRequest(request1) - XCTAssertEqual(request1Action.request, .executeRequest(request1, conn1, cancelTimeout: false)) - XCTAssertEqual(request1Action.connection, .cancelTimeoutTimer(conn1ID)) - - let goAwayAction = state.http2ConnectionGoAwayReceived(conn1ID) - XCTAssertEqual(goAwayAction.request, .none) - XCTAssertEqual(goAwayAction.connection, .none) - - // close stream - let closeStream1Action = state.http2ConnectionStreamClosed(conn1ID) - XCTAssertEqual(closeStream1Action.request, .none) - XCTAssertEqual(closeStream1Action.connection, .none, "Connection is automatically closed by HTTP2IdleHandler") - } - - func testGoAwayWithPendingRequestsStartsNewConnection() { - let elg = EmbeddedEventLoopGroup(loops: 1) - let el1 = elg.next() - - // establish one idle http2 connection - let idGenerator = HTTPConnectionPool.Connection.ID.Generator() - var http1Conns = HTTPConnectionPool.HTTP1Connections( - maximumConcurrentConnections: 8, - generator: idGenerator, - maximumConnectionUses: nil - ) - let conn1ID = http1Conns.createNewConnection(on: el1) - var state = HTTPConnectionPool.HTTP2StateMachine( - idGenerator: idGenerator, - retryConnectionEstablishment: true, - lifecycleState: .running, - maximumConnectionUses: nil - ) - - let conn1 = HTTPConnectionPool.Connection.__testOnly_connection(id: conn1ID, eventLoop: el1) - let connectAction1 = state.migrateFromHTTP1( - http1Connections: http1Conns, - requests: .init(), - newHTTP2Connection: conn1, - maxConcurrentStreams: 1 - ) - XCTAssertEqual(connectAction1.request, .none) - XCTAssertEqual( - connectAction1.connection, - .migration( - createConnections: [], - closeConnections: [], - scheduleTimeout: (conn1ID, el1) - ) - ) - - // execute request - let mockRequest1 = MockHTTPScheduableRequest(eventLoop: el1) - let request1 = HTTPConnectionPool.Request(mockRequest1) - let request1Action = state.executeRequest(request1) - XCTAssertEqual(request1Action.request, .executeRequest(request1, conn1, cancelTimeout: false)) - XCTAssertEqual(request1Action.connection, .cancelTimeoutTimer(conn1ID)) - - // queue request - let mockRequest2 = MockHTTPScheduableRequest(eventLoop: el1) - let request2 = HTTPConnectionPool.Request(mockRequest2) - let request2Action = state.executeRequest(request2) - XCTAssertEqual(request2Action.request, .scheduleRequestTimeout(for: request2, on: el1)) - XCTAssertEqual(request2Action.connection, .none) - - // go away should create a new connection - let goAwayAction = state.http2ConnectionGoAwayReceived(conn1ID) - XCTAssertEqual(goAwayAction.request, .none) - guard case .createConnection(let conn2ID, let eventLoop) = goAwayAction.connection else { - return XCTFail("unexpected connection action \(goAwayAction.connection)") - } - XCTAssertEqual(el1.id, eventLoop.id) - - // new connection should execute pending request - let conn2 = HTTPConnectionPool.Connection.__testOnly_connection(id: conn2ID, eventLoop: el1) - let connectAction2 = state.newHTTP2ConnectionEstablished(conn2, maxConcurrentStreams: 1) - XCTAssertEqual(connectAction2.request, .executeRequestsAndCancelTimeouts([request2], conn2)) - XCTAssertEqual(connectAction2.connection, .none) - - // close stream from conn1 - let closeStream1Action = state.http2ConnectionStreamClosed(conn1ID) - XCTAssertEqual(closeStream1Action.request, .none) - XCTAssertEqual(closeStream1Action.connection, .none, "Connection is automatically closed by HTTP2IdleHandler") - - // close stream from conn2 - let closeStream2Action = state.http2ConnectionStreamClosed(conn2ID) - XCTAssertEqual(closeStream2Action.request, .none) - XCTAssertEqual(closeStream2Action.connection, .scheduleTimeoutTimer(conn2ID, on: el1)) - } - - func testMigrationFromHTTP1ToHTTP2() { - let elg = EmbeddedEventLoopGroup(loops: 1) - let el1 = elg.next() - var connections = MockConnectionPool() - var queuer = MockRequestQueuer() - var state = HTTPConnectionPool.StateMachine( - idGenerator: .init(), - maximumConcurrentHTTP1Connections: 8, - retryConnectionEstablishment: true, - preferHTTP1: true, - maximumConnectionUses: nil - ) - - /// first 8 request should create a new connection - var connectionIDs: [HTTPConnectionPool.Connection.ID] = [] - for _ in 0..<8 { - let mockRequest = MockHTTPScheduableRequest(eventLoop: el1) - let request = HTTPConnectionPool.Request(mockRequest) - let action = state.executeRequest(request) - guard case .createConnection(let connID, let eventLoop) = action.connection else { - return XCTFail("Unexpected connection action \(action.connection)") - } - connectionIDs.append(connID) - XCTAssertTrue(eventLoop === el1) - XCTAssertEqual(action.request, .scheduleRequestTimeout(for: request, on: mockRequest.eventLoop)) - XCTAssertNoThrow(try connections.createConnection(connID, on: el1)) - XCTAssertNoThrow(try queuer.queue(mockRequest, id: request.id)) - } - - guard let conn1ID = connectionIDs.first else { - return XCTFail("could not create connection") - } - - /// after we reached the `maximumConcurrentHTTP1Connections`, we will not create new connections - for _ in 0..<8 { - let mockRequest = MockHTTPScheduableRequest(eventLoop: el1) - let request = HTTPConnectionPool.Request(mockRequest) - let action = state.executeRequest(request) - XCTAssertEqual(action.connection, .none) - XCTAssertEqual(action.request, .scheduleRequestTimeout(for: request, on: mockRequest.eventLoop)) - - XCTAssertNoThrow(try queuer.queue(mockRequest, id: request.id)) - } - - /// first new HTTP2 connection should migrate from HTTP1 to HTTP2 and execute requests - let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1) - XCTAssertNoThrow(try connections.succeedConnectionCreationHTTP2(conn1ID, maxConcurrentStreams: 10)) - let migrationAction = state.newHTTP2ConnectionCreated(conn1, maxConcurrentStreams: 10) - guard case .executeRequestsAndCancelTimeouts(let requests, let conn) = migrationAction.request else { - return XCTFail("unexpected request action \(migrationAction.request)") - } - XCTAssertEqual(conn, conn1) - XCTAssertEqual(requests.count, 10) - - for request in requests { - XCTAssertNoThrow(try queuer.get(request.id, request: request.__testOnly_wrapped_request())) - XCTAssertNoThrow(try connections.execute(request.__testOnly_wrapped_request(), on: conn1)) - } - - XCTAssertEqual( - migrationAction.connection, - .migration( - createConnections: [], - closeConnections: [], - scheduleTimeout: nil - ) - ) - - /// remaining connections should be closed immediately without executing any request - for connID in connectionIDs.dropFirst() { - let conn: HTTPConnectionPool.Connection = .__testOnly_connection(id: connID, eventLoop: el1) - XCTAssertNoThrow(try connections.succeedConnectionCreationHTTP2(connID, maxConcurrentStreams: 10)) - let action = state.newHTTP2ConnectionCreated(conn, maxConcurrentStreams: 10) - XCTAssertEqual(action.request, .none) - XCTAssertEqual(action.connection, .closeConnection(conn, isShutdown: .no)) - XCTAssertNoThrow(try connections.closeConnection(conn)) - } - - /// closing a stream while we have requests queued should result in one request execution action - for _ in 0..<6 { - XCTAssertNoThrow(try connections.finishExecution(conn1ID)) - let action = state.http2ConnectionStreamClosed(conn1ID) - XCTAssertEqual(action.connection, .none) - guard case .executeRequestsAndCancelTimeouts(let requests, conn) = action.request else { - return XCTFail("Unexpected request action \(action.request)") - } - XCTAssertEqual(requests.count, 1) - for request in requests { - XCTAssertNoThrow(try queuer.cancel(request.id)) - XCTAssertNoThrow(try connections.execute(request.__testOnly_wrapped_request(), on: conn1)) - } - } - XCTAssertTrue(queuer.isEmpty) - } - - func testMigrationFromHTTP1ToHTTP2WhileShuttingDown() { - let elg = EmbeddedEventLoopGroup(loops: 1) - let el1 = elg.next() - var connections = MockConnectionPool() - var queuer = MockRequestQueuer() - var state = HTTPConnectionPool.StateMachine( - idGenerator: .init(), - maximumConcurrentHTTP1Connections: 8, - retryConnectionEstablishment: true, - preferHTTP1: false, - maximumConnectionUses: nil - ) - - /// create a new connection - let mockRequest = MockHTTPScheduableRequest(eventLoop: el1) - let request = HTTPConnectionPool.Request(mockRequest) - let action = state.executeRequest(request) - guard case .createConnection(let conn1ID, let eventLoop) = action.connection else { - return XCTFail("Unexpected connection action \(action.connection)") - } - - XCTAssertTrue(eventLoop === el1) - XCTAssertEqual(action.request, .scheduleRequestTimeout(for: request, on: mockRequest.eventLoop)) - XCTAssertNoThrow(try connections.createConnection(conn1ID, on: el1)) - XCTAssertNoThrow(try queuer.queue(mockRequest, id: request.id)) - - /// we now no longer want anything of it - let shutdownAction = state.shutdown() - guard case .failRequestsAndCancelTimeouts(let requestsToCancel, let error) = shutdownAction.request else { - return XCTFail("unexpected shutdown action \(shutdownAction)") - } - XCTAssertEqualTypeAndValue(error, HTTPClientError.cancelled) - - for request in requestsToCancel { - XCTAssertNoThrow(try queuer.cancel(request.id)) - } - XCTAssertTrue(queuer.isEmpty) - - /// new HTTP2 connection should migrate from HTTP1 to HTTP2, close the connection and shutdown the pool - let conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: conn1ID, eventLoop: el1) - XCTAssertNoThrow(try connections.succeedConnectionCreationHTTP2(conn1ID, maxConcurrentStreams: 10)) - let migrationAction = state.newHTTP2ConnectionCreated(conn1, maxConcurrentStreams: 10) - XCTAssertEqual(migrationAction.request, .none) - XCTAssertEqual(migrationAction.connection, .closeConnection(conn1, isShutdown: .yes(unclean: true))) - XCTAssertNoThrow(try connections.closeConnection(conn1)) - XCTAssertTrue(connections.isEmpty) - } - - func testMigrationFromHTTP1ToHTTP2WithAlreadyStartedHTTP1Connections() { - let elg = EmbeddedEventLoopGroup(loops: 1) - let el1 = elg.next() - var connections = MockConnectionPool() - var queuer = MockRequestQueuer() - var state = HTTPConnectionPool.StateMachine( - idGenerator: .init(), - maximumConcurrentHTTP1Connections: 8, - retryConnectionEstablishment: true, - preferHTTP1: true, - maximumConnectionUses: nil - ) - - /// first 8 request should create a new connection - var connectionIDs: [HTTPConnectionPool.Connection.ID] = [] - for _ in 0..<8 { - let mockRequest = MockHTTPScheduableRequest(eventLoop: el1) - let request = HTTPConnectionPool.Request(mockRequest) - let action = state.executeRequest(request) - guard case .createConnection(let connID, let eventLoop) = action.connection else { - return XCTFail("Unexpected connection action \(action.connection)") - } - connectionIDs.append(connID) - XCTAssertTrue(eventLoop === el1) - XCTAssertEqual(action.request, .scheduleRequestTimeout(for: request, on: mockRequest.eventLoop)) - XCTAssertNoThrow(try connections.createConnection(connID, on: el1)) - XCTAssertNoThrow(try queuer.queue(mockRequest, id: request.id)) - } - - /// after we reached the `maximumConcurrentHTTP1Connections`, we will not create new connections - for _ in 0..<8 { - let mockRequest = MockHTTPScheduableRequest(eventLoop: el1) - let request = HTTPConnectionPool.Request(mockRequest) - let action = state.executeRequest(request) - XCTAssertEqual(action.connection, .none) - XCTAssertEqual(action.request, .scheduleRequestTimeout(for: request, on: mockRequest.eventLoop)) - XCTAssertNoThrow(try queuer.queue(mockRequest, id: request.id)) - } - - let http1ConnIDs = connectionIDs.prefix(4) - let succesfullHTTP1ConnIDs = http1ConnIDs.prefix(2) - let failedHTTP1ConnIDs = http1ConnIDs.dropFirst(2) - - /// new http1 connection should execute 1 request - for connID in succesfullHTTP1ConnIDs { - let conn: HTTPConnectionPool.Connection = .__testOnly_connection(id: connID, eventLoop: el1) - XCTAssertNoThrow(try connections.succeedConnectionCreationHTTP1(connID)) - let action = state.newHTTP1ConnectionCreated(conn) - guard case .executeRequest(let request, conn, cancelTimeout: true) = action.request else { - return XCTFail("unexpected request action \(action.request)") - } - XCTAssertEqual(action.connection, .none) - XCTAssertNoThrow(try queuer.get(request.id, request: request.__testOnly_wrapped_request())) - XCTAssertNoThrow(try connections.execute(request.__testOnly_wrapped_request(), on: conn)) - } - - /// failing connection should backoff connection - for connID in failedHTTP1ConnIDs { - XCTAssertNoThrow(try connections.failConnectionCreation(connID)) - struct SomeError: Error {} - let action = state.failedToCreateNewConnection(SomeError(), connectionID: connID) - guard case .scheduleBackoffTimer(connID, backoff: _, let el) = action.connection else { - return XCTFail("unexpected connection action \(action.connection)") - } - XCTAssertEqual(action.request, .none) - XCTAssertTrue(el === el1) - XCTAssertNoThrow(try connections.startConnectionBackoffTimer(connID)) - } - - let http2ConnectionIDs = Array(connectionIDs.dropFirst(4)) - - guard let firstHTTP2ConnID = http2ConnectionIDs.first else { - return XCTFail("could not create connection") - } - - /// first new HTTP2 connection should migrate from HTTP1 to HTTP2 and execute requests - let http2Conn: HTTPConnectionPool.Connection = .__testOnly_connection(id: firstHTTP2ConnID, eventLoop: el1) - XCTAssertNoThrow(try connections.succeedConnectionCreationHTTP2(firstHTTP2ConnID, maxConcurrentStreams: 10)) - let migrationAction = state.newHTTP2ConnectionCreated(http2Conn, maxConcurrentStreams: 10) - guard case .executeRequestsAndCancelTimeouts(let requests, let conn) = migrationAction.request else { - return XCTFail("unexpected request action \(migrationAction.request)") - } - XCTAssertEqual( - migrationAction.connection, - .migration(createConnections: [], closeConnections: [], scheduleTimeout: nil) - ) - - XCTAssertEqual(conn, http2Conn) - XCTAssertEqual(requests.count, 10) - - for request in requests { - XCTAssertNoThrow(try queuer.get(request.id, request: request.__testOnly_wrapped_request())) - XCTAssertNoThrow(try connections.execute(request.__testOnly_wrapped_request(), on: http2Conn)) - } - - /// remaining connections should be closed immediately without executing any request - for connID in http2ConnectionIDs.dropFirst() { - let conn: HTTPConnectionPool.Connection = .__testOnly_connection(id: connID, eventLoop: el1) - XCTAssertNoThrow(try connections.succeedConnectionCreationHTTP2(connID, maxConcurrentStreams: 10)) - let action = state.newHTTP2ConnectionCreated(conn, maxConcurrentStreams: 10) - XCTAssertEqual(action.request, .none) - XCTAssertEqual(action.connection, .closeConnection(conn, isShutdown: .no)) - XCTAssertNoThrow(try connections.closeConnection(conn)) - } - - /// after a request has finished on a http1 connection, the connection should be closed - /// because we are now in http/2 mode - for http1ConnectionID in succesfullHTTP1ConnIDs { - XCTAssertNoThrow(try connections.finishExecution(http1ConnectionID)) - let action = state.http1ConnectionReleased(http1ConnectionID) - XCTAssertEqual(action.request, .none) - guard case .closeConnection(let conn, isShutdown: .no) = action.connection else { - return XCTFail("unexpected connection action \(migrationAction.connection)") - } - XCTAssertEqual(conn.id, http1ConnectionID) - } - - /// if a backoff timer fires for an old http1 connection we should not start a new connection - /// because we are already in http2 mode - for http1ConnectionID in failedHTTP1ConnIDs { - XCTAssertNoThrow(try connections.connectionBackoffTimerDone(http1ConnectionID)) - let action = state.connectionCreationBackoffDone(http1ConnectionID) - XCTAssertEqual(action, .none) - } - - /// closing a stream while we have requests queued should result in one request execution action - for _ in 0..<4 { - XCTAssertNoThrow(try connections.finishExecution(http2Conn.id)) - let action = state.http2ConnectionStreamClosed(http2Conn.id) - XCTAssertEqual(action.connection, .none) - guard case .executeRequestsAndCancelTimeouts(let requests, http2Conn) = action.request else { - return XCTFail("Unexpected request action \(action.request)") - } - XCTAssertEqual(requests.count, 1) - for request in requests { - XCTAssertNoThrow(try queuer.get(request.id, request: request.__testOnly_wrapped_request())) - XCTAssertNoThrow(try connections.execute(request.__testOnly_wrapped_request(), on: http2Conn)) - } - } - - XCTAssertTrue(queuer.isEmpty) - } - - func testHTTP2toHTTP1Migration() { - let elg = EmbeddedEventLoopGroup(loops: 2) - let el1 = elg.next() - let el2 = elg.next() - var connections = MockConnectionPool() - var queuer = MockRequestQueuer() - var state = HTTPConnectionPool.StateMachine( - idGenerator: .init(), - maximumConcurrentHTTP1Connections: 8, - retryConnectionEstablishment: true, - preferHTTP1: false, - maximumConnectionUses: nil - ) - - // create http2 connection - let mockRequest = MockHTTPScheduableRequest(eventLoop: el1) - let request1 = HTTPConnectionPool.Request(mockRequest) - let action1 = state.executeRequest(request1) - guard case .createConnection(let http2ConnID, let http2EventLoop) = action1.connection else { - return XCTFail("Unexpected connection action \(action1.connection)") - } - XCTAssertTrue(http2EventLoop === el1) - XCTAssertEqual(action1.request, .scheduleRequestTimeout(for: request1, on: mockRequest.eventLoop)) - XCTAssertNoThrow(try connections.createConnection(http2ConnID, on: el1)) - XCTAssertNoThrow(try queuer.queue(mockRequest, id: request1.id)) - let http2Conn: HTTPConnectionPool.Connection = .__testOnly_connection(id: http2ConnID, eventLoop: el1) - XCTAssertNoThrow(try connections.succeedConnectionCreationHTTP2(http2ConnID, maxConcurrentStreams: 10)) - let executeAction1 = state.newHTTP2ConnectionCreated(http2Conn, maxConcurrentStreams: 10) - guard case .executeRequestsAndCancelTimeouts(let requests, http2Conn) = executeAction1.request else { - return XCTFail("unexpected request action \(executeAction1.request)") - } - - XCTAssertEqual(requests.count, 1) - for request in requests { - XCTAssertNoThrow(try queuer.get(request.id, request: request.__testOnly_wrapped_request())) - XCTAssertNoThrow(try connections.execute(request.__testOnly_wrapped_request(), on: http2Conn)) - } - - // a request with new required event loop should create a new connection - let mockRequestWithRequiredEventLoop = MockHTTPScheduableRequest( - eventLoop: el2, - requiresEventLoopForChannel: true - ) - let requestWithRequiredEventLoop = HTTPConnectionPool.Request(mockRequestWithRequiredEventLoop) - let action2 = state.executeRequest(requestWithRequiredEventLoop) - guard case .createConnection(let http1ConnId, let http1EventLoop) = action2.connection else { - return XCTFail("Unexpected connection action \(action2.connection)") - } - XCTAssertTrue(http1EventLoop === el2) - XCTAssertEqual( - action2.request, - .scheduleRequestTimeout(for: requestWithRequiredEventLoop, on: mockRequestWithRequiredEventLoop.eventLoop) - ) - XCTAssertNoThrow(try connections.createConnection(http1ConnId, on: el2)) - XCTAssertNoThrow(try queuer.queue(mockRequestWithRequiredEventLoop, id: requestWithRequiredEventLoop.id)) - - // if we established a new http/1 connection we should migrate back to http/1 - let http1Conn: HTTPConnectionPool.Connection = .__testOnly_connection(id: http1ConnId, eventLoop: el2) - XCTAssertNoThrow(try connections.succeedConnectionCreationHTTP1(http1ConnId)) - let migrationAction2 = state.newHTTP1ConnectionCreated(http1Conn) - guard case .executeRequest(let request2, http1Conn, cancelTimeout: true) = migrationAction2.request else { - return XCTFail("unexpected request action \(migrationAction2.request)") - } - guard - case .migration(let createConnections, closeConnections: [], scheduleTimeout: nil) = migrationAction2 - .connection - else { - return XCTFail("unexpected connection action \(migrationAction2.connection)") - } - XCTAssertEqual(createConnections.map { $0.1.id }, [el2.id]) - XCTAssertNoThrow(try queuer.get(request2.id, request: request2.__testOnly_wrapped_request())) - XCTAssertNoThrow(try connections.execute(request2.__testOnly_wrapped_request(), on: http1Conn)) - - // in http/1 state, we should close idle http2 connections - XCTAssertNoThrow(try connections.finishExecution(http2Conn.id)) - let releaseAction = state.http2ConnectionStreamClosed(http2Conn.id) - XCTAssertEqual(releaseAction.connection, .closeConnection(http2Conn, isShutdown: .no)) - XCTAssertEqual(releaseAction.request, .none) - XCTAssertNoThrow(try connections.closeConnection(http2Conn)) - } - - func testHTTP2toHTTP1MigrationDuringShutdown() { - let elg = EmbeddedEventLoopGroup(loops: 2) - let el1 = elg.next() - let el2 = elg.next() - var connections = MockConnectionPool() - var queuer = MockRequestQueuer() - var state = HTTPConnectionPool.StateMachine( - idGenerator: .init(), - maximumConcurrentHTTP1Connections: 8, - retryConnectionEstablishment: true, - preferHTTP1: false, - maximumConnectionUses: nil - ) - - // create http2 connection - let mockRequest = MockHTTPScheduableRequest(eventLoop: el1) - let request1 = HTTPConnectionPool.Request(mockRequest) - let action1 = state.executeRequest(request1) - guard case .createConnection(let http2ConnID, let http2EventLoop) = action1.connection else { - return XCTFail("Unexpected connection action \(action1.connection)") - } - XCTAssertTrue(http2EventLoop === el1) - XCTAssertEqual(action1.request, .scheduleRequestTimeout(for: request1, on: mockRequest.eventLoop)) - XCTAssertNoThrow(try connections.createConnection(http2ConnID, on: el1)) - XCTAssertNoThrow(try queuer.queue(mockRequest, id: request1.id)) - let http2Conn: HTTPConnectionPool.Connection = .__testOnly_connection(id: http2ConnID, eventLoop: el1) - XCTAssertNoThrow(try connections.succeedConnectionCreationHTTP2(http2ConnID, maxConcurrentStreams: 10)) - let executeAction1 = state.newHTTP2ConnectionCreated(http2Conn, maxConcurrentStreams: 10) - guard case .executeRequestsAndCancelTimeouts(let requests, http2Conn) = executeAction1.request else { - return XCTFail("unexpected request action \(executeAction1.request)") - } - - XCTAssertEqual(requests.count, 1) - for request in requests { - XCTAssertNoThrow(try queuer.get(request.id, request: request.__testOnly_wrapped_request())) - XCTAssertNoThrow(try connections.execute(request.__testOnly_wrapped_request(), on: http2Conn)) - } - - // a request with new required event loop should create a new connection - let mockRequestWithRequiredEventLoop = MockHTTPScheduableRequest( - eventLoop: el2, - requiresEventLoopForChannel: true - ) - let requestWithRequiredEventLoop = HTTPConnectionPool.Request(mockRequestWithRequiredEventLoop) - let action2 = state.executeRequest(requestWithRequiredEventLoop) - guard case .createConnection(let http1ConnId, let http1EventLoop) = action2.connection else { - return XCTFail("Unexpected connection action \(action2.connection)") - } - XCTAssertTrue(http1EventLoop === el2) - XCTAssertEqual( - action2.request, - .scheduleRequestTimeout(for: requestWithRequiredEventLoop, on: mockRequestWithRequiredEventLoop.eventLoop) - ) - XCTAssertNoThrow(try connections.createConnection(http1ConnId, on: el2)) - XCTAssertNoThrow(try queuer.queue(mockRequestWithRequiredEventLoop, id: requestWithRequiredEventLoop.id)) - - /// we now no longer want anything of it - let shutdownAction = state.shutdown() - guard case .failRequestsAndCancelTimeouts(let requestsToCancel, let error) = shutdownAction.request else { - return XCTFail("unexpected shutdown action \(shutdownAction)") - } - XCTAssertEqualTypeAndValue(error, HTTPClientError.cancelled) - - for request in requestsToCancel { - XCTAssertNoThrow(try queuer.cancel(request.id)) - } - XCTAssertTrue(queuer.isEmpty) - - // if we established a new http/1 connection we should migrate to http/1, - // close the connection and shutdown the pool - let http1Conn: HTTPConnectionPool.Connection = .__testOnly_connection(id: http1ConnId, eventLoop: el2) - XCTAssertNoThrow(try connections.succeedConnectionCreationHTTP1(http1ConnId)) - let migrationAction2 = state.newHTTP1ConnectionCreated(http1Conn) - XCTAssertEqual(migrationAction2.request, .none) - XCTAssertEqual( - migrationAction2.connection, - .migration(createConnections: [], closeConnections: [http1Conn], scheduleTimeout: nil) - ) - - // in http/1 state, we should close idle http2 connections - XCTAssertNoThrow(try connections.finishExecution(http2Conn.id)) - let releaseAction = state.http2ConnectionStreamClosed(http2Conn.id) - XCTAssertEqual(releaseAction.connection, .closeConnection(http2Conn, isShutdown: .yes(unclean: true))) - XCTAssertEqual(releaseAction.request, .none) - XCTAssertNoThrow(try connections.closeConnection(http2Conn)) - } - - func testConnectionIsImmediatelyCreatedAfterBackoffTimerFires() { - let elg = EmbeddedEventLoopGroup(loops: 2) - let el1 = elg.next() - let el2 = elg.next() - var connections = MockConnectionPool() - var queuer = MockRequestQueuer() - var state = HTTPConnectionPool.StateMachine( - idGenerator: .init(), - maximumConcurrentHTTP1Connections: 8, - retryConnectionEstablishment: true, - preferHTTP1: false, - maximumConnectionUses: nil - ) - - var connectionIDs: [HTTPConnectionPool.Connection.ID] = [] - for el in [el1, el2] { - let mockRequest = MockHTTPScheduableRequest(eventLoop: el, requiresEventLoopForChannel: true) - let request = HTTPConnectionPool.Request(mockRequest) - let action = state.executeRequest(request) - guard case .createConnection(let connID, let eventLoop) = action.connection else { - return XCTFail("Unexpected connection action \(action.connection)") - } - connectionIDs.append(connID) - XCTAssertTrue(eventLoop === el) - XCTAssertEqual(action.request, .scheduleRequestTimeout(for: request, on: mockRequest.eventLoop)) - XCTAssertNoThrow(try connections.createConnection(connID, on: el)) - XCTAssertNoThrow(try queuer.queue(mockRequest, id: request.id)) - } - - // fail the connection for el2 - for connectionID in connectionIDs.dropFirst() { - struct SomeError: Error {} - XCTAssertNoThrow(try connections.failConnectionCreation(connectionID)) - let action = state.failedToCreateNewConnection(SomeError(), connectionID: connectionID) - XCTAssertEqual(action.request, .none) - guard case .scheduleBackoffTimer(connectionID, backoff: _, on: _) = action.connection else { - return XCTFail("unexpected connection action \(connectionID)") - } - XCTAssertNoThrow(try connections.startConnectionBackoffTimer(connectionID)) - } - let http2ConnID1 = connectionIDs[0] - let http2ConnID2 = connectionIDs[1] - - // let the first connection on el1 succeed as a http2 connection - let http2Conn1: HTTPConnectionPool.Connection = .__testOnly_connection(id: http2ConnID1, eventLoop: el1) - XCTAssertNoThrow(try connections.succeedConnectionCreationHTTP2(http2ConnID1, maxConcurrentStreams: 10)) - let connectionAction = state.newHTTP2ConnectionCreated(http2Conn1, maxConcurrentStreams: 10) - guard case .executeRequestsAndCancelTimeouts(let requests, http2Conn1) = connectionAction.request else { - return XCTFail("unexpected request action \(connectionAction.request)") - } - XCTAssertEqual(requests.count, 1) - for request in requests { - XCTAssertNoThrow(try queuer.get(request.id, request: request.__testOnly_wrapped_request())) - XCTAssertNoThrow(try connections.execute(request.__testOnly_wrapped_request(), on: http2Conn1)) - } - - // we now have 1 active connection on el1 and 2 backing off connections on el2 - // with 2 queued requests with a requirement to be executed on el2 - - // if the backoff timer fires for a connection on el2, we should immediately start a new connection - XCTAssertNoThrow(try connections.connectionBackoffTimerDone(http2ConnID2)) - let action2 = state.connectionCreationBackoffDone(http2ConnID2) - XCTAssertEqual(action2.request, .none) - guard case .createConnection(let newHttp2ConnID2, let eventLoop2) = action2.connection else { - return XCTFail("Unexpected connection action \(action2.connection)") - } - XCTAssertTrue(eventLoop2 === el2) - XCTAssertNoThrow(try connections.createConnection(newHttp2ConnID2, on: el2)) - } - - func testMaxConcurrentStreamsIsRespected() { - let elg = EmbeddedEventLoopGroup(loops: 4) - defer { XCTAssertNoThrow(try elg.syncShutdownGracefully()) } - - guard var (connections, state) = try? MockConnectionPool.http2(elg: elg, maxConcurrentStreams: 100) else { - return XCTFail("Test setup failed") - } - - let generalPurposeConnection = connections.randomParkedConnection()! - var queuer = MockRequestQueuer() - - // schedule 1000 requests on the pool. The first 100 will be executed right away. All others - // shall be queued. - for i in 0..<1000 { - let requestEL = elg.next() - let mockRequest = MockHTTPScheduableRequest(eventLoop: requestEL) - let request = HTTPConnectionPool.Request(mockRequest) - - let executeAction = state.executeRequest(request) - switch i { - case 0: - XCTAssertEqual(executeAction.connection, .cancelTimeoutTimer(generalPurposeConnection.id)) - XCTAssertNoThrow(try connections.activateConnection(generalPurposeConnection.id)) - XCTAssertEqual( - executeAction.request, - .executeRequest(request, generalPurposeConnection, cancelTimeout: false) - ) - XCTAssertNoThrow(try connections.execute(mockRequest, on: generalPurposeConnection)) - case 1..<100: - XCTAssertEqual( - executeAction.request, - .executeRequest(request, generalPurposeConnection, cancelTimeout: false) - ) - XCTAssertEqual(executeAction.connection, .none) - XCTAssertNoThrow(try connections.execute(mockRequest, on: generalPurposeConnection)) - case 100..<1000: - XCTAssertEqual(executeAction.request, .scheduleRequestTimeout(for: request, on: requestEL)) - XCTAssertEqual(executeAction.connection, .none) - XCTAssertNoThrow(try queuer.queue(mockRequest, id: request.id)) - default: - XCTFail("Unexpected") - } - } - - // let's end processing 500 requests. For every finished request, we will execute another one - // right away - while queuer.count > 500 { - XCTAssertNoThrow(try connections.finishExecution(generalPurposeConnection.id)) - let finishAction = state.http2ConnectionStreamClosed(generalPurposeConnection.id) - XCTAssertEqual(finishAction.connection, .none) - guard case .executeRequestsAndCancelTimeouts(let requests, generalPurposeConnection) = finishAction.request - else { - return XCTFail("Unexpected request action: \(finishAction.request)") - } - guard requests.count == 1, let request = requests.first else { - return XCTFail("Expected to get exactly one request!") - } - let mockRequest = request.__testOnly_wrapped_request() - XCTAssertNoThrow(try queuer.get(request.id, request: mockRequest)) - XCTAssertNoThrow(try connections.execute(mockRequest, on: generalPurposeConnection)) - } - - XCTAssertEqual(queuer.count, 500) - - // Next the server allows for more concurrent streams - let newMaxStreams = 200 - XCTAssertNoThrow( - try connections.newHTTP2ConnectionSettingsReceived( - generalPurposeConnection.id, - maxConcurrentStreams: newMaxStreams - ) - ) - let newMaxStreamsAction = state.newHTTP2MaxConcurrentStreamsReceived( - generalPurposeConnection.id, - newMaxStreams: newMaxStreams - ) - XCTAssertEqual(newMaxStreamsAction.connection, .none) - guard - case .executeRequestsAndCancelTimeouts(let requests, generalPurposeConnection) = newMaxStreamsAction.request - else { - return XCTFail( - "Unexpected request action after new max concurrent stream setting: \(newMaxStreamsAction.request)" - ) - } - XCTAssertEqual(requests.count, 100, "Expected to execute 100 more requests") - for request in requests { - let mockRequest = request.__testOnly_wrapped_request() - XCTAssertNoThrow(try connections.execute(mockRequest, on: generalPurposeConnection)) - XCTAssertNoThrow(try queuer.get(request.id, request: mockRequest)) - } - - XCTAssertEqual(queuer.count, 400) - - // let's end processing 100 requests. For every finished request, we will execute another one - // right away - while queuer.count > 300 { - XCTAssertNoThrow(try connections.finishExecution(generalPurposeConnection.id)) - let finishAction = state.http2ConnectionStreamClosed(generalPurposeConnection.id) - XCTAssertEqual(finishAction.connection, .none) - guard case .executeRequestsAndCancelTimeouts(let requests, generalPurposeConnection) = finishAction.request - else { - return XCTFail("Unexpected request action: \(finishAction.request)") - } - guard requests.count == 1, let request = requests.first else { - return XCTFail("Expected to get exactly one request!") - } - let mockRequest = request.__testOnly_wrapped_request() - XCTAssertNoThrow(try queuer.get(request.id, request: mockRequest)) - XCTAssertNoThrow(try connections.execute(mockRequest, on: generalPurposeConnection)) - } - - // Next the server allows for fewer concurrent streams - let fewerMaxStreams = 50 - XCTAssertNoThrow( - try connections.newHTTP2ConnectionSettingsReceived( - generalPurposeConnection.id, - maxConcurrentStreams: fewerMaxStreams - ) - ) - let fewerMaxStreamsAction = state.newHTTP2MaxConcurrentStreamsReceived( - generalPurposeConnection.id, - newMaxStreams: fewerMaxStreams - ) - XCTAssertEqual(fewerMaxStreamsAction.connection, .none) - XCTAssertEqual(fewerMaxStreamsAction.request, .none) - - // for the next 150 requests that are finished, no new request must be executed. - for _ in 0..<150 { - XCTAssertNoThrow(try connections.finishExecution(generalPurposeConnection.id)) - XCTAssertEqual(state.http2ConnectionStreamClosed(generalPurposeConnection.id), .none) - } - - XCTAssertEqual(queuer.count, 300) - - // let's end all remaining requests. For every finished request, we will execute another one - // right away - while queuer.count > 0 { - XCTAssertNoThrow(try connections.finishExecution(generalPurposeConnection.id)) - let finishAction = state.http2ConnectionStreamClosed(generalPurposeConnection.id) - XCTAssertEqual(finishAction.connection, .none) - guard case .executeRequestsAndCancelTimeouts(let requests, generalPurposeConnection) = finishAction.request - else { - return XCTFail("Unexpected request action: \(finishAction.request)") - } - guard requests.count == 1, let request = requests.first else { - return XCTFail("Expected to get exactly one request!") - } - let mockRequest = request.__testOnly_wrapped_request() - XCTAssertNoThrow(try queuer.get(request.id, request: mockRequest)) - XCTAssertNoThrow(try connections.execute(mockRequest, on: generalPurposeConnection)) - } - - // Now we only need to drain the remaining 50 requests on the connection - var timeoutTimerScheduled = false - for remaining in stride(from: 50, through: 1, by: -1) { - XCTAssertNoThrow(try connections.finishExecution(generalPurposeConnection.id)) - let finishAction = state.http2ConnectionStreamClosed(generalPurposeConnection.id) - XCTAssertEqual(finishAction.request, .none) - switch remaining { - case 1: - timeoutTimerScheduled = true - XCTAssertEqual( - finishAction.connection, - .scheduleTimeoutTimer(generalPurposeConnection.id, on: generalPurposeConnection.eventLoop) - ) - XCTAssertNoThrow(try connections.parkConnection(generalPurposeConnection.id)) - case 2...50: - XCTAssertEqual(finishAction.connection, .none) - default: - XCTFail("Unexpected value: \(remaining)") - } - } - XCTAssertTrue(timeoutTimerScheduled) - XCTAssertNotNil(connections.randomParkedConnection()) - XCTAssertEqual(connections.count, 1) - } - - func testEventsAfterConnectionIsClosed() { - let elg = EmbeddedEventLoopGroup(loops: 2) - guard var (connections, state) = try? MockConnectionPool.http2(elg: elg, maxConcurrentStreams: 100) else { - return XCTFail("Test setup failed") - } - - let connection = connections.randomParkedConnection()! - XCTAssertNoThrow(try connections.closeConnection(connection)) - - let idleTimeoutAction = state.connectionIdleTimeout(connection.id) - XCTAssertEqual(idleTimeoutAction.connection, .closeConnection(connection, isShutdown: .no)) - XCTAssertEqual(idleTimeoutAction.request, .none) - - XCTAssertEqual(state.newHTTP2MaxConcurrentStreamsReceived(connection.id, newMaxStreams: 50), .none) - XCTAssertEqual(state.http2ConnectionGoAwayReceived(connection.id), .none) - - XCTAssertEqual(state.http2ConnectionClosed(connection.id), .none) - } -} - -/// Should be used if you have a value of statically unknown type and want to compare its value to another `Equatable` value. -/// The assert will fail if both values don't have the same type or don't have the same value. -/// - Note: if the type of both values are statically know, prefer `XCTAssertEqual`. -/// - Parameters: -/// - lhs: value of a statically unknown type -/// - rhs: value of statically known and `Equatable` type -func XCTAssertEqualTypeAndValue( - _ lhs: @autoclosure () throws -> Left, - _ rhs: @autoclosure () throws -> Right, - file: StaticString = #filePath, - line: UInt = #line -) { - XCTAssertNoThrow( - try { - let lhs = try lhs() - let rhs = try rhs() - guard let lhsAsRhs = lhs as? Right else { - XCTFail("could not cast \(lhs) of type \(type(of: lhs)) to \(type(of: rhs))", file: file, line: line) - return - } - XCTAssertEqual(lhsAsRhs, rhs, file: file, line: line) - }(), - file: file, - line: line - ) -} diff --git a/Tests/AsyncHTTPClientTests/HTTPConnectionPool+ManagerTests.swift b/Tests/AsyncHTTPClientTests/HTTPConnectionPool+ManagerTests.swift deleted file mode 100644 index 724c00b1f..000000000 --- a/Tests/AsyncHTTPClientTests/HTTPConnectionPool+ManagerTests.swift +++ /dev/null @@ -1,132 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOCore -import NIOHTTP1 -import NIOPosix -import XCTest - -@testable import AsyncHTTPClient - -class HTTPConnectionPool_ManagerTests: XCTestCase { - func testManagerHappyPath() { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 4) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let httpBin1 = HTTPBin() - defer { XCTAssertNoThrow(try httpBin1.shutdown()) } - - let httpBin2 = HTTPBin() - defer { XCTAssertNoThrow(try httpBin2.shutdown()) } - - let server = [httpBin1, httpBin2] - - let poolManager = HTTPConnectionPool.Manager( - eventLoopGroup: eventLoopGroup, - configuration: .init(), - backgroundActivityLogger: .init(label: "test") - ) - - defer { - let promise = eventLoopGroup.next().makePromise(of: Bool.self) - poolManager.shutdown(promise: promise) - XCTAssertNoThrow(try promise.futureResult.wait()) - } - - for i in 0..<9 { - let httpBin = server[i % 2] - - var maybeRequest: HTTPClient.Request? - var maybeRequestBag: RequestBag? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "http://localhost/(httpBin.port)")) - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: XCTUnwrap(maybeRequest), - eventLoopPreference: .indifferent, - task: .init(eventLoop: eventLoopGroup.next(), logger: .init(label: "test")), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(5), - requestOptions: .forTests(), - delegate: ResponseAccumulator(request: XCTUnwrap(maybeRequest)) - ) - ) - - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to get a request") } - - poolManager.executeRequest(requestBag) - - XCTAssertNoThrow(try requestBag.task.futureResult.wait()) - XCTAssertEqual(httpBin.activeConnections, 1) - } - } - - func testShutdownManagerThatHasSeenNoConnections() { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let poolManager = HTTPConnectionPool.Manager( - eventLoopGroup: eventLoopGroup, - configuration: .init(), - backgroundActivityLogger: .init(label: "test") - ) - - let eventLoop = eventLoopGroup.next() - let promise = eventLoop.makePromise(of: Bool.self) - poolManager.shutdown(promise: promise) - XCTAssertFalse(try promise.futureResult.wait()) - } - - func testExecutingARequestOnAShutdownPoolManager() { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let httpBin = HTTPBin() - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let poolManager = HTTPConnectionPool.Manager( - eventLoopGroup: eventLoopGroup, - configuration: .init(), - backgroundActivityLogger: .init(label: "test") - ) - - let eventLoop = eventLoopGroup.next() - let promise = eventLoop.makePromise(of: Bool.self) - poolManager.shutdown(promise: promise) - XCTAssertFalse(try promise.futureResult.wait()) - - var maybeRequest: HTTPClient.Request? - var maybeRequestBag: RequestBag? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "http://localhost/(httpBin.port)")) - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: XCTUnwrap(maybeRequest), - eventLoopPreference: .indifferent, - task: .init(eventLoop: eventLoopGroup.next(), logger: .init(label: "test")), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(5), - requestOptions: .forTests(), - delegate: ResponseAccumulator(request: XCTUnwrap(maybeRequest)) - ) - ) - - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to get a request") } - - poolManager.executeRequest(requestBag) - - XCTAssertThrowsError(try requestBag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .alreadyShutdown) - } - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTPConnectionPool+RequestQueueTests.swift b/Tests/AsyncHTTPClientTests/HTTPConnectionPool+RequestQueueTests.swift deleted file mode 100644 index 4f4bbd785..000000000 --- a/Tests/AsyncHTTPClientTests/HTTPConnectionPool+RequestQueueTests.swift +++ /dev/null @@ -1,140 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOCore -import NIOEmbedded -import NIOHTTP1 -import NIOSSL -import XCTest - -@testable import AsyncHTTPClient - -class HTTPConnectionPool_RequestQueueTests: XCTestCase { - func testCountAndIsEmptyWorks() { - var queue = HTTPConnectionPool.RequestQueue() - XCTAssertTrue(queue.isEmpty) - XCTAssertEqual(queue.count, 0) - let req1 = MockScheduledRequest(requiredEventLoop: nil) - let req1ID = queue.push(.init(req1)) - XCTAssertFalse(queue.isEmpty) - XCTAssertFalse(queue.isEmpty(for: nil)) - XCTAssertEqual(queue.count, 1) - XCTAssertEqual(queue.generalPurposeCount, 1) - - let req2 = MockScheduledRequest(requiredEventLoop: nil) - let req2ID = queue.push(.init(req2)) - XCTAssertEqual(queue.count, 2) - - XCTAssert(queue.popFirst()?.__testOnly_wrapped_request() === req1) - XCTAssertEqual(queue.count, 1) - XCTAssertFalse(queue.isEmpty) - XCTAssert(queue.remove(req2ID)?.__testOnly_wrapped_request() === req2) - XCTAssertNil(queue.remove(req1ID)) - XCTAssertEqual(queue.count, 0) - XCTAssertTrue(queue.isEmpty) - - let eventLoop = EmbeddedEventLoop() - - XCTAssertTrue(queue.isEmpty(for: eventLoop)) - XCTAssertEqual(queue.count(for: eventLoop), 0) - let req3 = MockScheduledRequest(requiredEventLoop: eventLoop) - let req3ID = queue.push(.init(req3)) - XCTAssertFalse(queue.isEmpty(for: eventLoop)) - XCTAssertEqual(queue.count(for: eventLoop), 1) - XCTAssertFalse(queue.isEmpty) - XCTAssertEqual(queue.count, 1) - XCTAssert(queue.popFirst(for: eventLoop)?.__testOnly_wrapped_request() === req3) - XCTAssertNil(queue.remove(req3ID)) - XCTAssertTrue(queue.isEmpty(for: eventLoop)) - XCTAssertEqual(queue.count(for: eventLoop), 0) - XCTAssertTrue(queue.isEmpty) - XCTAssertEqual(queue.count, 0) - - let req4 = MockScheduledRequest(requiredEventLoop: eventLoop) - let req4ID = queue.push(.init(req4)) - XCTAssert(queue.remove(req4ID)?.__testOnly_wrapped_request() === req4) - - let req5 = MockScheduledRequest(requiredEventLoop: nil) - queue.push(.init(req5)) - let req6 = MockScheduledRequest(requiredEventLoop: eventLoop) - queue.push(.init(req6)) - let all = queue.removeAll() - let testSet = all.map { $0.__testOnly_wrapped_request() } - XCTAssertEqual(testSet.count, 2) - XCTAssertTrue(testSet.contains(where: { $0 === req5 })) - XCTAssertTrue(testSet.contains(where: { $0 === req6 })) - XCTAssertFalse(testSet.contains(where: { $0 === req4 })) - XCTAssertTrue(queue.isEmpty(for: eventLoop)) - XCTAssertEqual(queue.count(for: eventLoop), 0) - XCTAssertTrue(queue.isEmpty) - XCTAssertEqual(queue.count, 0) - } -} - -final private class MockScheduledRequest: HTTPSchedulableRequest { - let requiredEventLoop: EventLoop? - - init(requiredEventLoop: EventLoop?) { - self.requiredEventLoop = requiredEventLoop - } - - var poolKey: ConnectionPool.Key { preconditionFailure("Unimplemented") } - var tlsConfiguration: TLSConfiguration? { nil } - var logger: Logger { preconditionFailure("Unimplemented") } - var connectionDeadline: NIODeadline { preconditionFailure("Unimplemented") } - var preferredEventLoop: EventLoop { preconditionFailure("Unimplemented") } - - func requestWasQueued(_: HTTPRequestScheduler) { - preconditionFailure("Unimplemented") - } - - func fail(_: Error) { - preconditionFailure("Unimplemented") - } - - // MARK: HTTPExecutableRequest - - var requestHead: HTTPRequestHead { preconditionFailure("Unimplemented") } - var requestFramingMetadata: RequestFramingMetadata { preconditionFailure("Unimplemented") } - var requestOptions: RequestOptions { preconditionFailure("Unimplemented") } - - func willExecuteRequest(_: HTTPRequestExecutor) { - preconditionFailure("Unimplemented") - } - - func requestHeadSent() { - preconditionFailure("Unimplemented") - } - - func resumeRequestBodyStream() { - preconditionFailure("Unimplemented") - } - - func pauseRequestBodyStream() { - preconditionFailure("Unimplemented") - } - - func receiveResponseHead(_: HTTPResponseHead) { - preconditionFailure("Unimplemented") - } - - func receiveResponseBodyParts(_: CircularBuffer) { - preconditionFailure("Unimplemented") - } - - func succeedRequest(_: CircularBuffer?) { - preconditionFailure("Unimplemented") - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTPConnectionPool+StateTestUtils.swift b/Tests/AsyncHTTPClientTests/HTTPConnectionPool+StateTestUtils.swift deleted file mode 100644 index bd9752d5d..000000000 --- a/Tests/AsyncHTTPClientTests/HTTPConnectionPool+StateTestUtils.swift +++ /dev/null @@ -1,189 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Atomics -import Dispatch -import NIOConcurrencyHelpers -import NIOCore -import NIOEmbedded - -@testable import AsyncHTTPClient - -/// An `EventLoopGroup` of `EmbeddedEventLoop`s. -final class EmbeddedEventLoopGroup: EventLoopGroup { - private let loops: [EmbeddedEventLoop] - private let index = ManagedAtomic(0) - - internal init(loops: Int) { - self.loops = (0.. EventLoop { - let index: Int = self.index.loadThenWrappingIncrement(ordering: .relaxed) - return self.loops[index % self.loops.count] - } - - internal func makeIterator() -> EventLoopIterator { - EventLoopIterator(self.loops) - } - - internal func shutdownGracefully(queue: DispatchQueue, _ callback: @escaping (Error?) -> Void) { - var shutdownError: Error? - - for loop in self.loops { - loop.shutdownGracefully(queue: queue) { error in - if let error = error { - shutdownError = error - } - } - } - - queue.sync { - callback(shutdownError) - } - } -} - -extension HTTPConnectionPool.Request: Equatable { - public static func == (lhs: Self, rhs: Self) -> Bool { - lhs.id == rhs.id - } -} - -extension HTTPConnectionPool.HTTP1Connections.ConnectionUse: Equatable { - public static func == (lhs: Self, rhs: Self) -> Bool { - switch (lhs, rhs) { - case (.eventLoop(let lhsEventLoop), .eventLoop(let rhsEventLoop)): - return lhsEventLoop === rhsEventLoop - case (.generalPurpose, .generalPurpose): - return true - default: - return false - } - } -} - -extension HTTPConnectionPool.StateMachine.ConnectionAction: Equatable { - public static func == (lhs: Self, rhs: Self) -> Bool { - switch (lhs, rhs) { - case (.createConnection(let lhsConnID, on: let lhsEL), .createConnection(let rhsConnID, on: let rhsEL)): - return lhsConnID == rhsConnID && lhsEL === rhsEL - case ( - .scheduleBackoffTimer(let lhsConnID, let lhsBackoff, on: let lhsEL), - .scheduleBackoffTimer(let rhsConnID, let rhsBackoff, on: let rhsEL) - ): - return lhsConnID == rhsConnID && lhsBackoff == rhsBackoff && lhsEL === rhsEL - case (.scheduleTimeoutTimer(let lhsConnID, on: let lhsEL), .scheduleTimeoutTimer(let rhsConnID, on: let rhsEL)): - return lhsConnID == rhsConnID && lhsEL === rhsEL - case (.cancelTimeoutTimer(let lhsConnID), .cancelTimeoutTimer(let rhsConnID)): - return lhsConnID == rhsConnID - case ( - .closeConnection(let lhsConn, isShutdown: let lhsShut), - .closeConnection(let rhsConn, isShutdown: let rhsShut) - ): - return lhsConn == rhsConn && lhsShut == rhsShut - case ( - .cleanupConnections(let lhsContext, isShutdown: let lhsShut), - .cleanupConnections(let rhsContext, isShutdown: let rhsShut) - ): - return lhsContext == rhsContext && lhsShut == rhsShut - case ( - .migration( - let lhsCreateConnections, - let lhsCloseConnections, - let lhsScheduleTimeout - ), - .migration( - let rhsCreateConnections, - let rhsCloseConnections, - let rhsScheduleTimeout - ) - ): - return lhsCreateConnections.elementsEqual( - rhsCreateConnections, - by: { - $0.0 == $1.0 && $0.1 === $1.1 - } - ) && lhsCloseConnections == rhsCloseConnections && lhsScheduleTimeout?.0 == rhsScheduleTimeout?.0 - && lhsScheduleTimeout?.1 === rhsScheduleTimeout?.1 - case (.none, .none): - return true - default: - return false - } - } -} - -extension HTTPConnectionPool.StateMachine.RequestAction: Equatable { - public static func == (lhs: Self, rhs: Self) -> Bool { - switch (lhs, rhs) { - case ( - .executeRequest(let lhsReq, let lhsConn, let lhsReqID), - .executeRequest(let rhsReq, let rhsConn, let rhsReqID) - ): - return lhsReq == rhsReq && lhsConn == rhsConn && lhsReqID == rhsReqID - case ( - .executeRequestsAndCancelTimeouts(let lhsReqs, let lhsConn), - .executeRequestsAndCancelTimeouts(let rhsReqs, let rhsConn) - ): - return lhsReqs.elementsEqual(rhsReqs, by: { $0 == $1 }) && lhsConn == rhsConn - case ( - .failRequest(let lhsReq, _, cancelTimeout: let lhsReqID), - .failRequest(let rhsReq, _, cancelTimeout: let rhsReqID) - ): - return lhsReq == rhsReq && lhsReqID == rhsReqID - case (.failRequestsAndCancelTimeouts(let lhsReqs, _), .failRequestsAndCancelTimeouts(let rhsReqs, _)): - return lhsReqs.elementsEqual(rhsReqs, by: { $0 == $1 }) - case ( - .scheduleRequestTimeout(for: let lhsReq, on: let lhsEL), - .scheduleRequestTimeout(for: let rhsReq, on: let rhsEL) - ): - return lhsReq == rhsReq && lhsEL === rhsEL - case (.none, .none): - return true - default: - return false - } - } -} - -extension HTTPConnectionPool.StateMachine.Action: Equatable { - public static func == (lhs: Self, rhs: Self) -> Bool { - lhs.connection == rhs.connection && lhs.request == rhs.request - } -} - -extension HTTPConnectionPool.HTTP2StateMachine.EstablishedConnectionAction: Equatable { - public static func == (lhs: Self, rhs: Self) -> Bool { - switch (lhs, rhs) { - case (.scheduleTimeoutTimer(let lhsConnID, on: let lhsEL), .scheduleTimeoutTimer(let rhsConnID, on: let rhsEL)): - return lhsConnID == rhsConnID && lhsEL === rhsEL - case ( - .closeConnection(let lhsConn, isShutdown: let lhsShut), - .closeConnection(let rhsConn, isShutdown: let rhsShut) - ): - return lhsConn == rhsConn && lhsShut == rhsShut - case (.none, .none): - return true - default: - return false - } - } -} - -extension HTTPConnectionPool.HTTP2StateMachine.EstablishedAction: Equatable { - public static func == (lhs: Self, rhs: Self) -> Bool { - lhs.connection == rhs.connection && lhs.request == rhs.request - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTPConnectionPoolTests.swift b/Tests/AsyncHTTPClientTests/HTTPConnectionPoolTests.swift deleted file mode 100644 index a40703456..000000000 --- a/Tests/AsyncHTTPClientTests/HTTPConnectionPoolTests.swift +++ /dev/null @@ -1,587 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOCore -import NIOHTTP1 -import NIOPosix -import XCTest - -@testable import AsyncHTTPClient - -class HTTPConnectionPoolTests: XCTestCase { - func testOnlyOneConnectionIsUsedForSubSequentRequests() { - let httpBin = HTTPBin() - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - let eventLoop = eventLoopGroup.next() - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let request = try! HTTPClient.Request(url: "http://localhost/(httpBin.port)") - let poolDelegate = TestDelegate(eventLoop: eventLoop) - - let pool = HTTPConnectionPool( - eventLoopGroup: eventLoopGroup, - sslContextCache: .init(), - tlsConfiguration: .none, - clientConfiguration: .init(), - key: .init(request), - delegate: poolDelegate, - idGenerator: .init(), - backgroundActivityLogger: .init(label: "test") - ) - defer { - pool.shutdown() - XCTAssertNoThrow(try poolDelegate.future.wait()) - XCTAssertNoThrow(try eventLoop.scheduleTask(in: .seconds(1)) {}.futureResult.wait()) - XCTAssertEqual(httpBin.activeConnections, 0) - } - - XCTAssertEqual(httpBin.createdConnections, 0) - - for _ in 0..<10 { - var maybeRequest: HTTPClient.Request? - var maybeRequestBag: RequestBag? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://localhost/(httpBin.port)")) - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: XCTUnwrap(maybeRequest), - eventLoopPreference: .indifferent, - task: .init(eventLoop: eventLoop, logger: .init(label: "test")), - redirectHandler: nil, - connectionDeadline: .distantFuture, - requestOptions: .forTests(), - delegate: ResponseAccumulator(request: XCTUnwrap(maybeRequest)) - ) - ) - - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to get a request") } - - pool.executeRequest(requestBag) - - XCTAssertNoThrow(try requestBag.task.futureResult.wait()) - XCTAssertEqual(httpBin.activeConnections, 1) - XCTAssertEqual(httpBin.createdConnections, 1) - } - } - - func testConnectionsForEventLoopRequirementsAreClosed() { - let httpBin = HTTPBin() - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2) - let eventLoop = eventLoopGroup.next() - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let request = try! HTTPClient.Request(url: "http://localhost/(httpBin.port)") - let poolDelegate = TestDelegate(eventLoop: eventLoop) - let pool = HTTPConnectionPool( - eventLoopGroup: eventLoopGroup, - sslContextCache: .init(), - tlsConfiguration: .none, - clientConfiguration: .init(), - key: .init(request), - delegate: poolDelegate, - idGenerator: .init(), - backgroundActivityLogger: .init(label: "test") - ) - defer { - pool.shutdown() - XCTAssertNoThrow(try poolDelegate.future.wait()) - XCTAssertNoThrow(try eventLoop.scheduleTask(in: .milliseconds(100)) {}.futureResult.wait()) - XCTAssertEqual(httpBin.activeConnections, 0) - // Since we would migrate from h2 -> h1, which creates a general purpose connection - // for every connection in .starting state, after the first request which will - // be serviced by an overflow connection, the rest of requests will use the general - // purpose connection since they are all on the same event loop. - // Hence we will only create 1 overflow connection and 1 general purpose connection. - XCTAssertEqual(httpBin.createdConnections, 2) - } - - XCTAssertEqual(httpBin.createdConnections, 0) - - for _ in 0..<10 { - var maybeRequest: HTTPClient.Request? - var maybeRequestBag: RequestBag? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://localhost/(httpBin.port)")) - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: XCTUnwrap(maybeRequest), - eventLoopPreference: .init( - .testOnly_exact(channelOn: eventLoopGroup.next(), delegateOn: eventLoopGroup.next()) - ), - task: .init(eventLoop: eventLoop, logger: .init(label: "test")), - redirectHandler: nil, - connectionDeadline: .distantFuture, - requestOptions: .forTests(), - delegate: ResponseAccumulator(request: XCTUnwrap(maybeRequest)) - ) - ) - - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to get a request") } - - pool.executeRequest(requestBag) - XCTAssertNoThrow(try requestBag.task.futureResult.wait()) - - // Flakiness Alert: We check <= and >= instead of == - // While migration from h2 -> h1, one general purpose and one over flow connection - // will be created, there's no guarantee as to whether the request is executed - // after both are created. - XCTAssertGreaterThanOrEqual(httpBin.createdConnections, 1) - XCTAssertLessThanOrEqual(httpBin.createdConnections, 2) - } - } - - func testConnectionsForEventLoopRequirementsAreClosedH1Only() { - let httpBin = HTTPBin() - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2) - let eventLoop = eventLoopGroup.next() - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let request = try! HTTPClient.Request(url: "http://localhost/(httpBin.port)") - let poolDelegate = TestDelegate(eventLoop: eventLoop) - var configuration = HTTPClient.Configuration() - configuration.httpVersion = .http1Only - let pool = HTTPConnectionPool( - eventLoopGroup: eventLoopGroup, - sslContextCache: .init(), - tlsConfiguration: .none, - clientConfiguration: configuration, - key: .init(request), - delegate: poolDelegate, - idGenerator: .init(), - backgroundActivityLogger: .init(label: "test") - ) - defer { - pool.shutdown() - XCTAssertNoThrow(try poolDelegate.future.wait()) - XCTAssertNoThrow(try eventLoop.scheduleTask(in: .milliseconds(100)) {}.futureResult.wait()) - XCTAssertEqual(httpBin.activeConnections, 0) - XCTAssertEqual(httpBin.createdConnections, 10) - } - - XCTAssertEqual(httpBin.createdConnections, 0) - - for i in 0..<10 { - var maybeRequest: HTTPClient.Request? - var maybeRequestBag: RequestBag? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://localhost/(httpBin.port)")) - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: XCTUnwrap(maybeRequest), - eventLoopPreference: .init( - .testOnly_exact(channelOn: eventLoopGroup.next(), delegateOn: eventLoopGroup.next()) - ), - task: .init(eventLoop: eventLoop, logger: .init(label: "test")), - redirectHandler: nil, - connectionDeadline: .distantFuture, - requestOptions: .forTests(), - delegate: ResponseAccumulator(request: XCTUnwrap(maybeRequest)) - ) - ) - - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to get a request") } - - pool.executeRequest(requestBag) - XCTAssertNoThrow(try requestBag.task.futureResult.wait()) - XCTAssertEqual(httpBin.createdConnections, i + 1) - } - } - - func testConnectionPoolGrowsToMaxConcurrentConnections() { - let httpBin = HTTPBin() - let maxConnections = 8 - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2) - let eventLoop = eventLoopGroup.next() - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let request = try! HTTPClient.Request(url: "http://localhost/(httpBin.port)") - let poolDelegate = TestDelegate(eventLoop: eventLoop) - - let pool = HTTPConnectionPool( - eventLoopGroup: eventLoopGroup, - sslContextCache: .init(), - tlsConfiguration: .none, - clientConfiguration: .init(connectionPool: .init(idleTimeout: .milliseconds(500))), - key: .init(request), - delegate: poolDelegate, - idGenerator: .init(), - backgroundActivityLogger: .init(label: "test") - ) - defer { - pool.shutdown() - XCTAssertNoThrow(try poolDelegate.future.wait()) - - XCTAssertEqual(httpBin.activeConnections, 0) - XCTAssertEqual(httpBin.createdConnections, maxConnections) - } - - XCTAssertEqual(httpBin.createdConnections, 0) - - var tasks = [EventLoopFuture]() - - for _ in 0..<1000 { - var maybeRequest: HTTPClient.Request? - var maybeRequestBag: RequestBag? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://localhost/(httpBin.port)")) - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: XCTUnwrap(maybeRequest), - eventLoopPreference: .indifferent, - task: .init(eventLoop: eventLoopGroup.next(), logger: .init(label: "test")), - redirectHandler: nil, - connectionDeadline: .distantFuture, - requestOptions: .forTests(), - delegate: ResponseAccumulator(request: XCTUnwrap(maybeRequest)) - ) - ) - - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to get a request") } - - pool.executeRequest(requestBag) - tasks.append(requestBag.task.futureResult) - } - - XCTAssertNoThrow(try EventLoopFuture.whenAllSucceed(tasks, on: eventLoopGroup.next()).wait()) - XCTAssertEqual(httpBin.activeConnections, maxConnections) - XCTAssertNoThrow(try eventLoop.scheduleTask(in: .milliseconds(600)) {}.futureResult.wait()) - XCTAssertEqual(httpBin.activeConnections, 0) - } - - func testConnectionCreationIsRetriedUntilRequestIsFailed() { - let httpBin = HTTPBin(proxy: .simulate(authorization: "abc123")) - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2) - let eventLoop = eventLoopGroup.next() - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let request = try! HTTPClient.Request(url: "http://localhost:9000/") - let poolDelegate = TestDelegate(eventLoop: eventLoop) - - let pool = HTTPConnectionPool( - eventLoopGroup: eventLoopGroup, - sslContextCache: .init(), - tlsConfiguration: .none, - clientConfiguration: .init( - proxy: .init(host: "localhost", port: httpBin.port, type: .http(.basic(credentials: "invalid"))) - ), - key: .init(request), - delegate: poolDelegate, - idGenerator: .init(), - backgroundActivityLogger: .init(label: "test") - ) - defer { - pool.shutdown() - XCTAssertNoThrow(try poolDelegate.future.wait()) - } - - XCTAssertEqual(httpBin.createdConnections, 0) - - var maybeRequest: HTTPClient.Request? - var maybeRequestBag: RequestBag? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://localhost/(httpBin.port)")) - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: XCTUnwrap(maybeRequest), - eventLoopPreference: .indifferent, - task: .init(eventLoop: eventLoopGroup.next(), logger: .init(label: "test")), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(5), - requestOptions: .forTests(), - delegate: ResponseAccumulator(request: XCTUnwrap(maybeRequest)) - ) - ) - - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to get a request") } - - pool.executeRequest(requestBag) - XCTAssertThrowsError(try requestBag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .proxyAuthenticationRequired) - } - XCTAssertGreaterThanOrEqual(httpBin.createdConnections, 8) - XCTAssertEqual(httpBin.activeConnections, 0) - } - - func testConnectionCreationIsRetriedUntilPoolIsShutdown() { - let httpBin = HTTPBin(proxy: .simulate(authorization: "abc123")) - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2) - let eventLoop = eventLoopGroup.next() - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let request = try! HTTPClient.Request(url: "http://localhost:9000/") - let poolDelegate = TestDelegate(eventLoop: eventLoop) - - let pool = HTTPConnectionPool( - eventLoopGroup: eventLoopGroup, - sslContextCache: .init(), - tlsConfiguration: .none, - clientConfiguration: .init( - proxy: .init(host: "localhost", port: httpBin.port, type: .http(.basic(credentials: "invalid"))) - ), - key: .init(request), - delegate: poolDelegate, - idGenerator: .init(), - backgroundActivityLogger: .init(label: "test") - ) - - XCTAssertEqual(httpBin.createdConnections, 0) - - var maybeRequest: HTTPClient.Request? - var maybeRequestBag: RequestBag? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://localhost/(httpBin.port)")) - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: XCTUnwrap(maybeRequest), - eventLoopPreference: .indifferent, - task: .init(eventLoop: eventLoopGroup.next(), logger: .init(label: "test")), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(5), - requestOptions: .forTests(), - delegate: ResponseAccumulator(request: XCTUnwrap(maybeRequest)) - ) - ) - - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to get a request") } - - pool.executeRequest(requestBag) - XCTAssertNoThrow(try eventLoop.scheduleTask(in: .seconds(2)) {}.futureResult.wait()) - pool.shutdown() - - XCTAssertThrowsError(try requestBag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .cancelled) - } - XCTAssertGreaterThanOrEqual(httpBin.createdConnections, 3) - XCTAssertNoThrow(try poolDelegate.future.wait()) - XCTAssertEqual(httpBin.activeConnections, 0) - } - - func testConnectionCreationIsRetriedUntilRequestIsCancelled() { - let httpBin = HTTPBin(proxy: .simulate(authorization: "abc123")) - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2) - let eventLoop = eventLoopGroup.next() - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let request = try! HTTPClient.Request(url: "http://localhost/(httpBin.port)") - let poolDelegate = TestDelegate(eventLoop: eventLoop) - - let pool = HTTPConnectionPool( - eventLoopGroup: eventLoopGroup, - sslContextCache: .init(), - tlsConfiguration: .none, - clientConfiguration: .init( - proxy: .init(host: "localhost", port: httpBin.port, type: .http(.basic(credentials: "invalid"))) - ), - key: .init(request), - delegate: poolDelegate, - idGenerator: .init(), - backgroundActivityLogger: .init(label: "test") - ) - defer { - pool.shutdown() - XCTAssertNoThrow(try poolDelegate.future.wait()) - } - - XCTAssertEqual(httpBin.createdConnections, 0) - - var maybeRequest: HTTPClient.Request? - var maybeRequestBag: RequestBag? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "http://localhost/(httpBin.port)")) - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: XCTUnwrap(maybeRequest), - eventLoopPreference: .indifferent, - task: .init(eventLoop: eventLoopGroup.next(), logger: .init(label: "test")), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(5), - requestOptions: .forTests(), - delegate: ResponseAccumulator(request: XCTUnwrap(maybeRequest)) - ) - ) - - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to get a request") } - - pool.executeRequest(requestBag) - XCTAssertNoThrow(try eventLoop.scheduleTask(in: .seconds(1)) {}.futureResult.wait()) - requestBag.fail(HTTPClientError.cancelled) - - XCTAssertThrowsError(try requestBag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .cancelled) - } - XCTAssertGreaterThanOrEqual(httpBin.createdConnections, 3) - } - - func testConnectionShutdownIsCalledOnActiveConnections() { - let httpBin = HTTPBin() - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - let eventLoop = eventLoopGroup.next() - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let request = try! HTTPClient.Request(url: "http://localhost/(httpBin.port)") - let poolDelegate = TestDelegate(eventLoop: eventLoop) - - let pool = HTTPConnectionPool( - eventLoopGroup: eventLoopGroup, - sslContextCache: .init(), - tlsConfiguration: .none, - clientConfiguration: .init(), - key: .init(request), - delegate: poolDelegate, - idGenerator: .init(), - backgroundActivityLogger: .init(label: "test") - ) - - var maybeRequest: HTTPClient.Request? - var maybeRequestBag: RequestBag? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "http://localhost/(httpBin.port)/wait")) - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: XCTUnwrap(maybeRequest), - eventLoopPreference: .indifferent, - task: .init(eventLoop: eventLoopGroup.next(), logger: .init(label: "test")), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(5), - requestOptions: .forTests(), - delegate: ResponseAccumulator(request: XCTUnwrap(maybeRequest)) - ) - ) - - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to get a request") } - - pool.executeRequest(requestBag) - XCTAssertNoThrow(try eventLoop.scheduleTask(in: .milliseconds(500)) {}.futureResult.wait()) - pool.shutdown() - - XCTAssertNoThrow(try poolDelegate.future.wait()) - - XCTAssertThrowsError(try requestBag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .cancelled) - } - - XCTAssertGreaterThanOrEqual(httpBin.createdConnections, 1) - XCTAssertGreaterThanOrEqual(httpBin.activeConnections, 0) - } - - func testConnectionPoolStressResistanceHTTP1() { - let numberOfRequestsPerThread = 1000 - let numberOfParallelWorkers = 8 - - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 8) - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let httpBin = HTTPBin() - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let logger = Logger(label: "Test") - let request = try! HTTPClient.Request(url: "http://localhost/(httpBin.port)") - let poolDelegate = TestDelegate(eventLoop: eventLoopGroup.next()) - - let pool = HTTPConnectionPool( - eventLoopGroup: eventLoopGroup, - sslContextCache: .init(), - tlsConfiguration: nil, - clientConfiguration: .init(), - key: .init(request), - delegate: poolDelegate, - idGenerator: .init(), - backgroundActivityLogger: logger - ) - - let dispatchGroup = DispatchGroup() - for workerID in 0..? - - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: url)) - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: XCTUnwrap(maybeRequest), - eventLoopPreference: .indifferent, - task: .init(eventLoop: eventLoopGroup.next(), logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(5), - requestOptions: .forTests(), - delegate: ResponseAccumulator(request: XCTUnwrap(maybeRequest)) - ) - ) - - guard let requestBag = maybeRequestBag else { return XCTFail("Expected to get a request") } - pool.executeRequest(requestBag) - - XCTAssertNoThrow(try requestBag.task.futureResult.wait()) - } - - for _ in 0.. - var future: EventLoopFuture { - self.promise.futureResult - } - - init(eventLoop: EventLoop) { - self.promise = eventLoop.makePromise(of: Bool.self) - } - - func connectionPoolDidShutdown(_ pool: HTTPConnectionPool, unclean: Bool) { - self.promise.succeed(unclean) - } -} diff --git a/Tests/AsyncHTTPClientTests/HTTPRequestStateMachineTests.swift b/Tests/AsyncHTTPClientTests/HTTPRequestStateMachineTests.swift deleted file mode 100644 index 8fe879745..000000000 --- a/Tests/AsyncHTTPClientTests/HTTPRequestStateMachineTests.swift +++ /dev/null @@ -1,1086 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOEmbedded -import NIOHTTP1 -import NIOSSL -import XCTest - -@testable import AsyncHTTPClient - -class HTTPRequestStateMachineTests: XCTestCase { - func testSimpleGETRequest() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: true) - ) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - let responseBody = ByteBuffer(bytes: [1, 2, 3, 4]) - XCTAssertEqual(state.channelRead(.body(responseBody)), .wait) - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.none, .init([responseBody]))) - XCTAssertEqual(state.channelReadComplete(), .wait) - } - - func testPOSTRequestWithWriterBackpressure() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead( - version: .http1_1, - method: .POST, - uri: "/", - headers: HTTPHeaders([("content-length", "4")]) - ) - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(4)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: false) - ) - XCTAssertEqual( - state.headSent(), - .notifyRequestHeadSendSuccessfully(resumeRequestBodyStream: true, startIdleTimer: false) - ) - let part0 = IOData.byteBuffer(ByteBuffer(bytes: [0])) - let part1 = IOData.byteBuffer(ByteBuffer(bytes: [1])) - let part2 = IOData.byteBuffer(ByteBuffer(bytes: [2])) - let part3 = IOData.byteBuffer(ByteBuffer(bytes: [3])) - XCTAssertEqual(state.requestStreamPartReceived(part0, promise: nil), .sendBodyPart(part0, nil)) - XCTAssertEqual(state.requestStreamPartReceived(part1, promise: nil), .sendBodyPart(part1, nil)) - - // oh the channel reports... we should slow down producing... - XCTAssertEqual(state.writabilityChanged(writable: false), .pauseRequestBodyStream) - - // but we issued a .produceMoreRequestBodyData before... Thus, we must accept more produced - // data - XCTAssertEqual(state.requestStreamPartReceived(part2, promise: nil), .sendBodyPart(part2, nil)) - // however when we have put the data on the channel, we should not issue further - // .produceMoreRequestBodyData events - - // once we receive a writable event again, we can allow the producer to produce more data - XCTAssertEqual(state.writabilityChanged(writable: true), .resumeRequestBodyStream) - XCTAssertEqual(state.requestStreamPartReceived(part3, promise: nil), .sendBodyPart(part3, nil)) - XCTAssertEqual(state.requestStreamFinished(promise: nil), .sendRequestEnd(nil)) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - let responseBody = ByteBuffer(bytes: [1, 2, 3, 4]) - XCTAssertEqual(state.channelRead(.body(responseBody)), .wait) - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.none, .init([responseBody]))) - XCTAssertEqual(state.channelReadComplete(), .wait) - } - - func testPOSTContentLengthIsTooLong() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead( - version: .http1_1, - method: .POST, - uri: "/", - headers: HTTPHeaders([("content-length", "4")]) - ) - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(4)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: false) - ) - let part0 = IOData.byteBuffer(ByteBuffer(bytes: [0, 1, 2, 3])) - let part1 = IOData.byteBuffer(ByteBuffer(bytes: [0, 1, 2, 3])) - XCTAssertEqual(state.requestStreamPartReceived(part0, promise: nil), .sendBodyPart(part0, nil)) - - state.requestStreamPartReceived(part1, promise: nil).assertFailRequest( - HTTPClientError.bodyLengthMismatch, - .close(nil) - ) - - // if another error happens the new one is ignored - XCTAssertEqual(state.errorHappened(HTTPClientError.remoteConnectionClosed), .wait) - } - - func testPOSTContentLengthIsTooShort() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead( - version: .http1_1, - method: .POST, - uri: "/", - headers: HTTPHeaders([("content-length", "8")]) - ) - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(8)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: false) - ) - let part0 = IOData.byteBuffer(ByteBuffer(bytes: [0, 1, 2, 3])) - XCTAssertEqual(state.requestStreamPartReceived(part0, promise: nil), .sendBodyPart(part0, nil)) - - state.requestStreamFinished(promise: nil).assertFailRequest(HTTPClientError.bodyLengthMismatch, .close(nil)) - } - - func testRequestBodyStreamIsCancelledIfServerRespondsWith301() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead( - version: .http1_1, - method: .POST, - uri: "/", - headers: HTTPHeaders([("content-length", "12")]) - ) - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(12)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: false) - ) - XCTAssertEqual( - state.headSent(), - .notifyRequestHeadSendSuccessfully(resumeRequestBodyStream: true, startIdleTimer: false) - ) - let part = IOData.byteBuffer(ByteBuffer(bytes: [0, 1, 2, 3])) - XCTAssertEqual(state.requestStreamPartReceived(part, promise: nil), .sendBodyPart(part, nil)) - - // response is coming before having send all data - let responseHead = HTTPResponseHead(version: .http1_1, status: .movedPermanently) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: true) - ) - XCTAssertEqual(state.writabilityChanged(writable: false), .wait) - XCTAssertEqual(state.writabilityChanged(writable: true), .wait) - XCTAssertEqual( - state.requestStreamPartReceived(part, promise: nil), - .failSendBodyPart(HTTPClientError.requestStreamCancelled, nil), - "Expected to drop all stream data after having received a response head, with status >= 300" - ) - - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.close, .init())) - - XCTAssertEqual( - state.requestStreamPartReceived(part, promise: nil), - .failSendBodyPart(HTTPClientError.requestStreamCancelled, nil), - "Expected to drop all stream data after having received a response head, with status >= 300" - ) - - XCTAssertEqual( - state.requestStreamFinished(promise: nil), - .failSendStreamFinished(HTTPClientError.requestStreamCancelled, nil), - "Expected to drop all stream data after having received a response head, with status >= 300" - ) - } - - func testStreamPartReceived_whenCancelled() { - var state = HTTPRequestStateMachine(isChannelWritable: false) - let part = IOData.byteBuffer(ByteBuffer(bytes: [0, 1, 2, 3])) - - XCTAssertEqual(state.requestCancelled(), .failRequest(HTTPClientError.cancelled, .none)) - XCTAssertEqual( - state.requestStreamPartReceived(part, promise: nil), - .failSendBodyPart(HTTPClientError.cancelled, nil), - "Expected to drop all stream data after having received a response head, with status >= 300" - ) - } - - func testRequestBodyStreamIsCancelledIfServerRespondsWith301WhileWriteBackpressure() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead( - version: .http1_1, - method: .POST, - uri: "/", - headers: HTTPHeaders([("content-length", "12")]) - ) - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(12)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: false) - ) - XCTAssertEqual( - state.headSent(), - .notifyRequestHeadSendSuccessfully(resumeRequestBodyStream: true, startIdleTimer: false) - ) - let part = IOData.byteBuffer(ByteBuffer(bytes: [0, 1, 2, 3])) - XCTAssertEqual(state.requestStreamPartReceived(part, promise: nil), .sendBodyPart(part, nil)) - XCTAssertEqual(state.writabilityChanged(writable: false), .pauseRequestBodyStream) - - // response is coming before having send all data - let responseHead = HTTPResponseHead(version: .http1_1, status: .movedPermanently) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - XCTAssertEqual(state.writabilityChanged(writable: true), .wait) - XCTAssertEqual( - state.requestStreamPartReceived(part, promise: nil), - .failSendBodyPart(HTTPClientError.requestStreamCancelled, nil), - "Expected to drop all stream data after having received a response head, with status >= 300" - ) - - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.close, .init())) - - XCTAssertEqual( - state.requestStreamPartReceived(part, promise: nil), - .failSendBodyPart(HTTPClientError.requestStreamCancelled, nil), - "Expected to drop all stream data after having received a response head, with status >= 300" - ) - - XCTAssertEqual( - state.requestStreamFinished(promise: nil), - .failSendStreamFinished(HTTPClientError.requestStreamCancelled, nil), - "Expected to drop all stream data after having received a response head, with status >= 300" - ) - } - - func testRequestBodyStreamIsContinuedIfServerRespondsWith200() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead( - version: .http1_1, - method: .POST, - uri: "/", - headers: HTTPHeaders([("content-length", "12")]) - ) - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(12)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: false) - ) - let part0 = IOData.byteBuffer(ByteBuffer(bytes: 0...3)) - XCTAssertEqual(state.requestStreamPartReceived(part0, promise: nil), .sendBodyPart(part0, nil)) - - // response is coming before having send all data - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - XCTAssertEqual(state.channelRead(.end(nil)), .forwardResponseBodyParts(.init())) - - let part1 = IOData.byteBuffer(ByteBuffer(bytes: 4...7)) - XCTAssertEqual(state.requestStreamPartReceived(part1, promise: nil), .sendBodyPart(part1, nil)) - let part2 = IOData.byteBuffer(ByteBuffer(bytes: 8...11)) - XCTAssertEqual(state.requestStreamPartReceived(part2, promise: nil), .sendBodyPart(part2, nil)) - XCTAssertEqual(state.requestStreamFinished(promise: nil), .succeedRequest(.sendRequestEnd(nil), .init())) - - XCTAssertEqual( - state.requestStreamPartReceived(part2, promise: nil), - .failSendBodyPart(HTTPClientError.requestStreamCancelled, nil) - ) - } - - func testRequestBodyStreamIsContinuedIfServerSendHeadWithStatus200() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead( - version: .http1_1, - method: .POST, - uri: "/", - headers: HTTPHeaders([("content-length", "12")]) - ) - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(12)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: false) - ) - let part0 = IOData.byteBuffer(ByteBuffer(bytes: 0...3)) - XCTAssertEqual(state.requestStreamPartReceived(part0, promise: nil), .sendBodyPart(part0, nil)) - - // response is coming before having send all data - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - - let part1 = IOData.byteBuffer(ByteBuffer(bytes: 4...7)) - XCTAssertEqual(state.requestStreamPartReceived(part1, promise: nil), .sendBodyPart(part1, nil)) - let part2 = IOData.byteBuffer(ByteBuffer(bytes: 8...11)) - XCTAssertEqual(state.requestStreamPartReceived(part2, promise: nil), .sendBodyPart(part2, nil)) - XCTAssertEqual(state.requestStreamFinished(promise: nil), .sendRequestEnd(nil)) - - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.none, .init())) - } - - func testRequestIsFailedIfRequestBodySizeIsWrongEvenAfterServerRespondedWith200() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead( - version: .http1_1, - method: .POST, - uri: "/", - headers: HTTPHeaders([("content-length", "12")]) - ) - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(12)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: false) - ) - let part0 = IOData.byteBuffer(ByteBuffer(bytes: 0...3)) - XCTAssertEqual(state.requestStreamPartReceived(part0, promise: nil), .sendBodyPart(part0, nil)) - - // response is coming before having send all data - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - XCTAssertEqual(state.channelRead(.end(nil)), .forwardResponseBodyParts(.init())) - - let part1 = IOData.byteBuffer(ByteBuffer(bytes: 4...7)) - XCTAssertEqual(state.requestStreamPartReceived(part1, promise: nil), .sendBodyPart(part1, nil)) - state.requestStreamFinished(promise: nil).assertFailRequest(HTTPClientError.bodyLengthMismatch, .close(nil)) - XCTAssertEqual(state.channelInactive(), .wait) - } - - func testRequestIsFailedIfRequestBodySizeIsWrongEvenAfterServerSendHeadWithStatus200() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead( - version: .http1_1, - method: .POST, - uri: "/", - headers: HTTPHeaders([("content-length", "12")]) - ) - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(12)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: false) - ) - let part0 = IOData.byteBuffer(ByteBuffer(bytes: 0...3)) - XCTAssertEqual(state.requestStreamPartReceived(part0, promise: nil), .sendBodyPart(part0, nil)) - - // response is coming before having send all data - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - - let part1 = IOData.byteBuffer(ByteBuffer(bytes: 4...7)) - XCTAssertEqual(state.requestStreamPartReceived(part1, promise: nil), .sendBodyPart(part1, nil)) - state.requestStreamFinished(promise: nil).assertFailRequest(HTTPClientError.bodyLengthMismatch, .close(nil)) - XCTAssertEqual(state.channelRead(.end(nil)), .wait) - } - - func testRequestIsNotSendUntilChannelIsWritable() { - var state = HTTPRequestStateMachine(isChannelWritable: false) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual(state.startRequest(head: requestHead, metadata: metadata), .wait) - XCTAssertEqual(state.read(), .read) - XCTAssertEqual(state.writabilityChanged(writable: true), .sendRequestHead(requestHead, sendEnd: true)) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - let responseBody = ByteBuffer(bytes: [1, 2, 3, 4]) - XCTAssertEqual(state.channelRead(.body(responseBody)), .wait) - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.none, .init([responseBody]))) - XCTAssertEqual(state.channelInactive(), .wait) - } - - func testConnectionBecomesInactiveWhileWaitingForWritable() { - var state = HTTPRequestStateMachine(isChannelWritable: false) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual(state.startRequest(head: requestHead, metadata: metadata), .wait) - state.channelInactive().assertFailRequest(HTTPClientError.remoteConnectionClosed, .none) - } - - func testResponseReadingWithBackpressure() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: true) - ) - - let responseHead = HTTPResponseHead( - version: .http1_1, - status: .ok, - headers: HTTPHeaders([("content-length", "12")]) - ) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - let part0 = ByteBuffer(bytes: 0...3) - let part1 = ByteBuffer(bytes: 4...7) - let part2 = ByteBuffer(bytes: 8...11) - XCTAssertEqual(state.channelRead(.body(part0)), .wait) - XCTAssertEqual(state.channelRead(.body(part1)), .wait) - XCTAssertEqual(state.channelReadComplete(), .forwardResponseBodyParts(.init([part0, part1]))) - XCTAssertEqual(state.read(), .wait) - XCTAssertEqual(state.read(), .wait, "Expected to be able to consume a second read event") - XCTAssertEqual(state.demandMoreResponseBodyParts(), .read) - XCTAssertEqual(state.channelRead(.body(part2)), .wait) - XCTAssertEqual(state.channelReadComplete(), .forwardResponseBodyParts(.init([part2]))) - XCTAssertEqual(state.demandMoreResponseBodyParts(), .wait) - XCTAssertEqual(state.read(), .read) - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.none, .init())) - XCTAssertEqual(state.channelReadComplete(), .wait) - XCTAssertEqual(state.read(), .read) - XCTAssertEqual(state.demandMoreResponseBodyParts(), .wait) - } - - func testChannelReadCompleteTriggersButNoBodyDataWasReceivedSoFar() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: true) - ) - - let responseHead = HTTPResponseHead( - version: .http1_1, - status: .ok, - headers: HTTPHeaders([("content-length", "12")]) - ) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - let part0 = ByteBuffer(bytes: 0...3) - let part1 = ByteBuffer(bytes: 4...7) - let part2 = ByteBuffer(bytes: 8...11) - XCTAssertEqual(state.channelReadComplete(), .wait) - XCTAssertEqual(state.read(), .read) - XCTAssertEqual(state.channelRead(.body(part0)), .wait) - XCTAssertEqual(state.channelRead(.body(part1)), .wait) - XCTAssertEqual(state.channelReadComplete(), .forwardResponseBodyParts(.init([part0, part1]))) - XCTAssertEqual(state.read(), .wait) - XCTAssertEqual(state.demandMoreResponseBodyParts(), .read) - XCTAssertEqual(state.channelReadComplete(), .wait) - XCTAssertEqual(state.read(), .read) - XCTAssertEqual(state.channelRead(.body(part2)), .wait) - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.none, .init([part2]))) - XCTAssertEqual(state.channelReadComplete(), .wait) - XCTAssertEqual(state.read(), .read) - XCTAssertEqual(state.demandMoreResponseBodyParts(), .wait) - } - - func testResponseReadingWithBackpressureEndOfResponseAllowsReadEventsToTriggerDirectly() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: true) - ) - - let responseHead = HTTPResponseHead( - version: .http1_1, - status: .ok, - headers: HTTPHeaders([("content-length", "12")]) - ) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - let part0 = ByteBuffer(bytes: 0...3) - let part1 = ByteBuffer(bytes: 4...7) - let part2 = ByteBuffer(bytes: 8...11) - XCTAssertEqual(state.channelRead(.body(part0)), .wait) - XCTAssertEqual(state.channelReadComplete(), .forwardResponseBodyParts(.init([part0]))) - XCTAssertEqual(state.read(), .wait) - XCTAssertEqual(state.demandMoreResponseBodyParts(), .read) - XCTAssertEqual(state.channelRead(.body(part1)), .wait) - XCTAssertEqual(state.channelReadComplete(), .forwardResponseBodyParts(.init([part1]))) - XCTAssertEqual(state.demandMoreResponseBodyParts(), .wait) - XCTAssertEqual(state.demandMoreResponseBodyParts(), .wait, "Calling forward more bytes twice is okay") - XCTAssertEqual(state.read(), .read) - XCTAssertEqual(state.channelRead(.body(part2)), .wait) - XCTAssertEqual(state.read(), .read, "Calling `read` while we wait for a channelReadComplete doesn't crash") - XCTAssertEqual( - state.demandMoreResponseBodyParts(), - .wait, - "Calling `demandMoreResponseBodyParts` while we wait for a channelReadComplete doesn't crash" - ) - XCTAssertEqual(state.channelReadComplete(), .forwardResponseBodyParts(.init([part2]))) - XCTAssertEqual(state.demandMoreResponseBodyParts(), .wait) - XCTAssertEqual(state.read(), .read) - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.none, .init())) - XCTAssertEqual(state.demandMoreResponseBodyParts(), .wait) - XCTAssertEqual(state.read(), .read) - } - - func testCancellingARequestInStateInitializedKeepsTheConnectionAlive() { - var state = HTTPRequestStateMachine(isChannelWritable: false) - state.requestCancelled().assertFailRequest(HTTPClientError.cancelled, .none) - } - - func testCancellingARequestBeforeBeingSendKeepsTheConnectionAlive() { - var state = HTTPRequestStateMachine(isChannelWritable: false) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual(state.startRequest(head: requestHead, metadata: metadata), .wait) - state.requestCancelled().assertFailRequest(HTTPClientError.cancelled, .none) - } - - func testConnectionBecomesWritableBeforeFirstRequest() { - var state = HTTPRequestStateMachine(isChannelWritable: false) - XCTAssertEqual(state.writabilityChanged(writable: true), .wait) - - // --- sending request - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: true) - ) - - // --- receiving response - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok, headers: ["content-length": "4"]) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - let responseBody = ByteBuffer(bytes: [1, 2, 3, 4]) - XCTAssertEqual(state.channelRead(.body(responseBody)), .wait) - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.none, .init([responseBody]))) - XCTAssertEqual(state.channelReadComplete(), .wait) - } - - func testCancellingARequestThatIsSent() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: true) - ) - state.requestCancelled().assertFailRequest(HTTPClientError.cancelled, .close(nil)) - } - - func testRemoteSuddenlyClosesTheConnection() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead( - version: .http1_1, - method: .GET, - uri: "/", - headers: .init([("content-length", "4")]) - ) - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(4)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: false) - ) - state.requestCancelled().assertFailRequest(HTTPClientError.cancelled, .close(nil)) - XCTAssertEqual( - state.requestStreamPartReceived(.byteBuffer(.init(bytes: 1...3)), promise: nil), - .failSendBodyPart(HTTPClientError.cancelled, nil) - ) - } - - func testReadTimeoutLeadsToFailureWithEverythingAfterBeingIgnored() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: true) - ) - - let responseHead = HTTPResponseHead( - version: .http1_1, - status: .ok, - headers: HTTPHeaders([("content-length", "12")]) - ) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - let part0 = ByteBuffer(bytes: 0...3) - XCTAssertEqual(state.channelRead(.body(part0)), .wait) - state.idleReadTimeoutTriggered().assertFailRequest(HTTPClientError.readTimeout, .close(nil)) - XCTAssertEqual(state.channelRead(.body(ByteBuffer(bytes: 4...7))), .wait) - XCTAssertEqual(state.channelRead(.body(ByteBuffer(bytes: 8...11))), .wait) - XCTAssertEqual(state.demandMoreResponseBodyParts(), .wait) - XCTAssertEqual(state.channelRead(.end(nil)), .wait) - } - - func testResponseWithStatus1XXAreIgnored() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: true) - ) - - let continueHead = HTTPResponseHead(version: .http1_1, status: .continue) - XCTAssertEqual(state.channelRead(.head(continueHead)), .wait) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.none, .init())) - XCTAssertEqual(state.channelReadComplete(), .wait) - XCTAssertEqual(state.read(), .read) - } - - func testReadTimeoutThatFiresToLateIsIgnored() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: true) - ) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.none, .init())) - XCTAssertEqual(state.idleReadTimeoutTriggered(), .wait, "A read timeout that fires to late must be ignored") - } - - func testCancellationThatIsInvokedToLateIsIgnored() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: true) - ) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.none, .init())) - XCTAssertEqual(state.requestCancelled(), .wait, "A cancellation that happens to late is ignored") - } - - func testErrorWhileRunningARequestClosesTheStream() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: true) - ) - - state.errorHappened(HTTPParserError.invalidChunkSize).assertFailRequest( - HTTPParserError.invalidChunkSize, - .close(nil) - ) - XCTAssertEqual(state.requestCancelled(), .wait, "A cancellation that happens to late is ignored") - } - - func testCanReadHTTP1_0ResponseWithoutBody() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: true) - ) - - let responseHead = HTTPResponseHead(version: .http1_0, status: .internalServerError) - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - XCTAssertEqual(state.demandMoreResponseBodyParts(), .wait) - XCTAssertEqual(state.channelReadComplete(), .wait) - XCTAssertEqual(state.read(), .read) - XCTAssertEqual(state.channelReadComplete(), .wait) - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.close, [])) - XCTAssertEqual(state.channelInactive(), .wait) - } - - func testCanReadHTTP1_0ResponseWithBody() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: true) - ) - - let responseHead = HTTPResponseHead(version: .http1_0, status: .internalServerError) - let body = ByteBuffer(string: "foo bar") - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - XCTAssertEqual(state.demandMoreResponseBodyParts(), .wait) - XCTAssertEqual(state.channelReadComplete(), .wait) - XCTAssertEqual(state.read(), .read) - XCTAssertEqual(state.channelReadComplete(), .wait) - XCTAssertEqual(state.channelRead(.body(body)), .wait) - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.close, [body])) - XCTAssertEqual(state.channelInactive(), .wait) - } - - func testFailHTTP1_0RequestThatIsStillUploading() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead(version: .http1_1, method: .POST, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .stream) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: false) - ) - - let part1: ByteBuffer = .init(string: "foo") - XCTAssertEqual( - state.requestStreamPartReceived(.byteBuffer(part1), promise: nil), - .sendBodyPart(.byteBuffer(part1), nil) - ) - let responseHead = HTTPResponseHead(version: .http1_0, status: .ok) - let body = ByteBuffer(string: "foo bar") - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - XCTAssertEqual(state.demandMoreResponseBodyParts(), .wait) - XCTAssertEqual(state.channelReadComplete(), .wait) - XCTAssertEqual(state.read(), .read) - XCTAssertEqual(state.channelReadComplete(), .wait) - XCTAssertEqual(state.channelRead(.body(body)), .wait) - state.channelRead(.end(nil)).assertFailRequest(HTTPClientError.remoteConnectionClosed, .close(nil)) - XCTAssertEqual(state.channelInactive(), .wait) - } - - func testFailHTTP1RequestWithoutContentLengthWithNIOSSLErrorUncleanShutdown() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: true) - ) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok) - let body = ByteBuffer(string: "foo bar") - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - XCTAssertEqual(state.demandMoreResponseBodyParts(), .wait) - XCTAssertEqual(state.channelRead(.body(body)), .wait) - state.errorHappened(NIOSSLError.uncleanShutdown).assertFailRequest(NIOSSLError.uncleanShutdown, .close(nil)) - XCTAssertEqual(state.channelRead(.end(nil)), .wait) - XCTAssertEqual(state.channelInactive(), .wait) - } - - func testNIOSSLErrorUncleanShutdownShouldBeTreatedAsRemoteConnectionCloseWhileInWaitingForHeadState() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: true) - ) - - XCTAssertEqual(state.errorHappened(NIOSSLError.uncleanShutdown), .wait) - state.channelInactive().assertFailRequest(HTTPClientError.remoteConnectionClosed, .none) - } - - func testArbitraryErrorShouldBeTreatedAsARequestFailureWhileInWaitingForHeadState() { - struct ArbitraryError: Error, Equatable {} - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: true) - ) - - state.errorHappened(ArbitraryError()).assertFailRequest(ArbitraryError(), .close(nil)) - XCTAssertEqual(state.channelInactive(), .wait) - } - - func testFailHTTP1RequestWithContentLengthWithNIOSSLErrorUncleanShutdownButIgnoreIt() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: true) - ) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok, headers: ["content-length": "30"]) - let body = ByteBuffer(string: "foo bar") - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - XCTAssertEqual(state.demandMoreResponseBodyParts(), .wait) - XCTAssertEqual(state.read(), .read) - XCTAssertEqual(state.channelRead(.body(body)), .wait) - XCTAssertEqual(state.channelReadComplete(), .forwardResponseBodyParts([body])) - XCTAssertEqual(state.errorHappened(NIOSSLError.uncleanShutdown), .wait) - state.errorHappened(HTTPParserError.invalidEOFState).assertFailRequest( - HTTPParserError.invalidEOFState, - .close(nil) - ) - XCTAssertEqual(state.channelInactive(), .wait) - } - - func testFailHTTPRequestWithContentLengthBecauseOfChannelInactiveWaitingForDemand() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: true) - ) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok, headers: ["Content-Length": "50"]) - let body = ByteBuffer(string: "foo bar") - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - XCTAssertEqual(state.demandMoreResponseBodyParts(), .wait) - XCTAssertEqual(state.channelReadComplete(), .wait) - XCTAssertEqual(state.read(), .read) - XCTAssertEqual(state.channelRead(.body(body)), .wait) - XCTAssertEqual(state.channelReadComplete(), .forwardResponseBodyParts([body])) - XCTAssertEqual(state.read(), .wait) - - XCTAssertEqual(state.channelRead(.body(ByteBuffer(string: " baz lightyear"))), .wait) - XCTAssertEqual(state.channelReadComplete(), .wait) - state.channelInactive().assertFailRequest(HTTPClientError.remoteConnectionClosed, .none) - } - - func testFailHTTPRequestWithContentLengthBecauseOfChannelInactiveWaitingForRead() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: true) - ) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok, headers: ["Content-Length": "50"]) - let body = ByteBuffer(string: "foo bar") - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - XCTAssertEqual(state.demandMoreResponseBodyParts(), .wait) - XCTAssertEqual(state.channelReadComplete(), .wait) - XCTAssertEqual(state.read(), .read) - XCTAssertEqual(state.channelRead(.body(body)), .wait) - XCTAssertEqual(state.channelReadComplete(), .forwardResponseBodyParts([body])) - XCTAssertEqual(state.demandMoreResponseBodyParts(), .wait) - - XCTAssertEqual(state.channelRead(.body(ByteBuffer(string: " baz lightyear"))), .wait) - XCTAssertEqual(state.channelReadComplete(), .wait) - state.channelInactive().assertFailRequest(HTTPClientError.remoteConnectionClosed, .none) - } - - func testFailHTTPRequestWithContentLengthBecauseOfChannelInactiveWaitingForReadAndDemand() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: true) - ) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok, headers: ["Content-Length": "50"]) - let body = ByteBuffer(string: "foo bar") - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - XCTAssertEqual(state.demandMoreResponseBodyParts(), .wait) - XCTAssertEqual(state.channelReadComplete(), .wait) - XCTAssertEqual(state.read(), .read) - XCTAssertEqual(state.channelRead(.body(body)), .wait) - XCTAssertEqual(state.channelReadComplete(), .forwardResponseBodyParts([body])) - - XCTAssertEqual(state.channelRead(.body(ByteBuffer(string: " baz lightyear"))), .wait) - XCTAssertEqual(state.channelReadComplete(), .wait) - state.channelInactive().assertFailRequest(HTTPClientError.remoteConnectionClosed, .none) - } - - func testFailHTTPRequestWithContentLengthBecauseOfChannelInactiveWaitingForReadAndDemandMultipleTimes() { - var state = HTTPRequestStateMachine(isChannelWritable: true) - let requestHead = HTTPRequestHead(version: .http1_1, method: .GET, uri: "/") - let metadata = RequestFramingMetadata(connectionClose: false, body: .fixedSize(0)) - XCTAssertEqual( - state.startRequest(head: requestHead, metadata: metadata), - .sendRequestHead(requestHead, sendEnd: true) - ) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok, headers: ["Content-Length": "50"]) - let body = ByteBuffer(string: "foo bar") - XCTAssertEqual( - state.channelRead(.head(responseHead)), - .forwardResponseHead(responseHead, pauseRequestBodyStream: false) - ) - XCTAssertEqual(state.demandMoreResponseBodyParts(), .wait) - XCTAssertEqual(state.channelReadComplete(), .wait) - XCTAssertEqual(state.read(), .read) - XCTAssertEqual(state.channelRead(.body(body)), .wait) - XCTAssertEqual(state.channelReadComplete(), .forwardResponseBodyParts([body])) - - let part1 = ByteBuffer(string: "baz lightyear") - XCTAssertEqual(state.channelRead(.body(part1)), .wait) - XCTAssertEqual(state.channelReadComplete(), .wait) - - let part2 = ByteBuffer(string: "nearly last") - XCTAssertEqual(state.channelRead(.body(part2)), .wait) - XCTAssertEqual(state.channelReadComplete(), .wait) - - let part3 = ByteBuffer(string: "final message") - XCTAssertEqual(state.channelRead(.body(part3)), .wait) - XCTAssertEqual(state.channelReadComplete(), .wait) - - XCTAssertEqual(state.channelRead(.end(nil)), .succeedRequest(.close, [part1, part2, part3])) - XCTAssertEqual(state.channelReadComplete(), .wait) - - XCTAssertEqual(state.channelInactive(), .wait) - } -} - -extension HTTPRequestStateMachine.Action: Equatable { - public static func == (lhs: HTTPRequestStateMachine.Action, rhs: HTTPRequestStateMachine.Action) -> Bool { - switch (lhs, rhs) { - case (.sendRequestHead(let lhsHead, let lhsStartBody), .sendRequestHead(let rhsHead, let rhsStartBody)): - return lhsHead == rhsHead && lhsStartBody == rhsStartBody - - case ( - .notifyRequestHeadSendSuccessfully(let lhsResumeRequestBodyStream, let lhsStartIdleTimer), - .notifyRequestHeadSendSuccessfully(let rhsResumeRequestBodyStream, let rhsStartIdleTimer) - ): - return lhsResumeRequestBodyStream == rhsResumeRequestBodyStream && lhsStartIdleTimer == rhsStartIdleTimer - - case (.sendBodyPart(let lhsData, let lhsPromise), .sendBodyPart(let rhsData, let rhsPromise)): - return lhsData == rhsData && lhsPromise?.futureResult == rhsPromise?.futureResult - - case (.sendRequestEnd(let lhsPromise), .sendRequestEnd(let rhsPromise)): - return lhsPromise?.futureResult == rhsPromise?.futureResult - - case (.pauseRequestBodyStream, .pauseRequestBodyStream): - return true - case (.resumeRequestBodyStream, .resumeRequestBodyStream): - return true - - case ( - .forwardResponseHead(let lhsHead, let lhsPauseRequestBodyStream), - .forwardResponseHead(let rhsHead, let rhsPauseRequestBodyStream) - ): - return lhsHead == rhsHead && lhsPauseRequestBodyStream == rhsPauseRequestBodyStream - - case (.forwardResponseBodyParts(let lhsData), .forwardResponseBodyParts(let rhsData)): - return lhsData == rhsData - - case ( - .succeedRequest(let lhsFinalAction, let lhsFinalBuffer), - .succeedRequest(let rhsFinalAction, let rhsFinalBuffer) - ): - return lhsFinalAction == rhsFinalAction && lhsFinalBuffer == rhsFinalBuffer - - case (.failRequest(_, let lhsFinalAction), .failRequest(_, let rhsFinalAction)): - return lhsFinalAction == rhsFinalAction - - case (.read, .read): - return true - - case (.wait, .wait): - return true - - case ( - .failSendBodyPart(let lhsError as HTTPClientError, let lhsPromise), - .failSendBodyPart(let rhsError as HTTPClientError, let rhsPromise) - ): - return lhsError == rhsError && lhsPromise?.futureResult == rhsPromise?.futureResult - - case ( - .failSendStreamFinished(let lhsError as HTTPClientError, let lhsPromise), - .failSendStreamFinished(let rhsError as HTTPClientError, let rhsPromise) - ): - return lhsError == rhsError && lhsPromise?.futureResult == rhsPromise?.futureResult - - default: - return false - } - } -} - -extension HTTPRequestStateMachine.Action.FinalSuccessfulRequestAction: Equatable { - public static func == ( - lhs: HTTPRequestStateMachine.Action.FinalSuccessfulRequestAction, - rhs: HTTPRequestStateMachine.Action.FinalSuccessfulRequestAction - ) -> Bool { - switch (lhs, rhs) { - case (.close, close): - return true - - case (.sendRequestEnd(let lhsPromise), .sendRequestEnd(let rhsPromise)): - return lhsPromise?.futureResult == rhsPromise?.futureResult - - case (.none, .none): - return true - - default: - return false - } - } -} - -extension HTTPRequestStateMachine.Action.FinalFailedRequestAction: Equatable { - public static func == ( - lhs: HTTPRequestStateMachine.Action.FinalFailedRequestAction, - rhs: HTTPRequestStateMachine.Action.FinalFailedRequestAction - ) -> Bool { - switch (lhs, rhs) { - case (.close(let lhsPromise), close(let rhsPromise)): - return lhsPromise?.futureResult == rhsPromise?.futureResult - - case (.none, .none): - return true - - default: - return false - } - } -} - -extension HTTPRequestStateMachine.Action { - fileprivate func assertFailRequest( - _ expectedError: Error, - _ expectedFinalStreamAction: HTTPRequestStateMachine.Action.FinalFailedRequestAction, - file: StaticString = #filePath, - line: UInt = #line - ) where Error: Swift.Error & Equatable { - guard case .failRequest(let actualError, let actualFinalStreamAction) = self else { - return XCTFail( - "expected .failRequest(\(expectedError), \(expectedFinalStreamAction)) but got \(self)", - file: file, - line: line - ) - } - if let actualError = actualError as? Error { - XCTAssertEqual(actualError, expectedError, file: file, line: line) - } else { - XCTFail("\(actualError) is not equal to \(expectedError)", file: file, line: line) - } - XCTAssertEqual(actualFinalStreamAction, expectedFinalStreamAction, file: file, line: line) - } -} diff --git a/Tests/AsyncHTTPClientTests/IdleTimeoutNoReuseTests.swift b/Tests/AsyncHTTPClientTests/IdleTimeoutNoReuseTests.swift deleted file mode 100644 index e9a0d46dc..000000000 --- a/Tests/AsyncHTTPClientTests/IdleTimeoutNoReuseTests.swift +++ /dev/null @@ -1,41 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2022 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import AsyncHTTPClient -import Atomics -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOFoundationCompat -import NIOHTTP1 -import NIOHTTPCompression -import NIOPosix -import NIOSSL -import NIOTestUtils -import NIOTransportServices -import XCTest - -#if canImport(Network) -import Network -#endif - -final class TestIdleTimeoutNoReuse: XCTestCaseHTTPClientTestsBaseClass { - func testIdleTimeoutNoReuse() throws { - var req = try HTTPClient.Request(url: self.defaultHTTPBinURLPrefix + "get", method: .GET) - XCTAssertNoThrow(try self.defaultClient.execute(request: req, deadline: .now() + .seconds(2)).wait()) - req.headers.add(name: "X-internal-delay", value: "2500") - try self.defaultClient.eventLoopGroup.next().scheduleTask(in: .milliseconds(250)) {}.futureResult.wait() - XCTAssertNoThrow(try self.defaultClient.execute(request: req).timeout(after: .seconds(10)).wait()) - } -} diff --git a/Tests/AsyncHTTPClientTests/LRUCacheTests.swift b/Tests/AsyncHTTPClientTests/LRUCacheTests.swift deleted file mode 100644 index 6173c34eb..000000000 --- a/Tests/AsyncHTTPClientTests/LRUCacheTests.swift +++ /dev/null @@ -1,84 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import XCTest - -@testable import AsyncHTTPClient - -class LRUCacheTests: XCTestCase { - func testBasicsWork() { - var cache = LRUCache(capacity: 1) - var requestedValueGens = 0 - for i in 0..<10 { - let actual = cache.findOrAppend(key: i) { i in - requestedValueGens += 1 - return i - } - XCTAssertEqual(i, actual) - } - XCTAssertEqual(10, requestedValueGens) - - let nine = cache.findOrAppend(key: 9) { i in - XCTAssertEqual(9, i) - XCTFail("9 should be in the cache") - return -1 - } - XCTAssertEqual(9, nine) - } - - func testCachesTheRightThings() { - var cache = LRUCache(capacity: 3) - - for i in 0..<10 { - let actual = cache.findOrAppend(key: i) { i in - i - } - XCTAssertEqual(i, actual) - - let zero = cache.find(key: 0) - XCTAssertEqual(0, zero, "at \(i), couldn't find 0") - - cache.append(key: -1, value: -1) - XCTAssertEqual(-1, cache.find(key: -1)) - } - - XCTAssertEqual(0, cache.find(key: 0)) - XCTAssertEqual(9, cache.find(key: 9)) - - for i in 1..<9 { - XCTAssertNil(cache.find(key: i)) - } - } - - func testAppendingTheSameDoesNotEvictButUpdates() { - var cache = LRUCache(capacity: 3) - - cache.append(key: 1, value: 1) - cache.append(key: 3, value: 3) - for i in (2...100).reversed() { - cache.append(key: 2, value: i) - XCTAssertEqual(i, cache.find(key: 2)) - } - - for i in 1...3 { - XCTAssertEqual(i, cache.find(key: i)) - } - - cache.append(key: 4, value: 4) - XCTAssertNil(cache.find(key: 1)) - for i in 2...4 { - XCTAssertEqual(i, cache.find(key: i)) - } - } -} diff --git a/Tests/AsyncHTTPClientTests/Mocks/MockConnectionPool.swift b/Tests/AsyncHTTPClientTests/Mocks/MockConnectionPool.swift deleted file mode 100644 index e49c67f19..000000000 --- a/Tests/AsyncHTTPClientTests/Mocks/MockConnectionPool.swift +++ /dev/null @@ -1,761 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOCore -import NIOHTTP1 -import NIOSSL - -@testable import AsyncHTTPClient - -/// A mock connection pool (not creating any actual connections) that is used to validate -/// connection actions returned by the `HTTPConnectionPool.StateMachine`. -struct MockConnectionPool { - typealias Connection = HTTPConnectionPool.Connection - - enum Errors: Error, Hashable { - case connectionIDAlreadyUsed - case connectionNotFound - case connectionExists - case connectionNotIdle - case connectionNotParked - case connectionIsParked - case connectionIsClosed - case connectionIsNotStarting - case connectionIsNotExecuting - case connectionDoesNotFulfillEventLoopRequirement - case connectionIsNotActive - case connectionIsNotHTTP2Connection - case connectionDoesNotHaveHTTP2StreamAvailable - case connectionBackoffTimerExists - case connectionBackoffTimerNotFound - } - - fileprivate struct MockConnectionState { - typealias ID = HTTPConnectionPool.Connection.ID - - private enum State { - // A note about idle vs. parked connections - // - // In our state machine we differentiate the concept of a connection being idle vs. it - // being parked. An idle connection is a connection that we are not executing any - // request on. A parked connection is an idle connection that will remain in this state - // for a longer period of time. For parked connections we create idle timeout timers. - // - // Consider those two flows to better understand the difference: - // - // 1. A connection becomes `idle` and there is more work queued. This will lead to a new - // request being executed on the connection. It will switch back to be in use without - // having been parked in the meantime. - // - // 2. A connection becomes `idle` and there is no more work queued. We don't want to get - // rid of the connection right away. For this reason we create an idle timeout timer - // for the connection. After having created the timer we consider the connection - // being parked. If a new request arrives, before the connection timed out, we need - // to cancel the idle timeout timer. - - enum HTTP1State { - case inUse - case idle(parked: Bool, idleSince: NIODeadline) - } - - enum HTTP2State { - case inUse(maxConcurrentStreams: Int, used: Int) - case idle(maxConcurrentStreams: Int, parked: Bool, lastIdle: NIODeadline) - } - - case starting - case http1(HTTP1State) - case http2(HTTP2State) - case closed - } - - let id: ID - let eventLoop: EventLoop - - private var state: State = .starting - - init(id: ID, eventLoop: EventLoop) { - self.id = id - self.eventLoop = eventLoop - } - - var isStarting: Bool { - switch self.state { - case .starting: - return true - default: - return false - } - } - - /// Is the connection idle (meaning there are no requests executing on it) - var isIdle: Bool { - switch self.state { - case .starting, .closed, .http1(.inUse), .http2(.inUse): - return false - - case .http1(.idle), .http2(.idle): - return true - } - } - - /// Is the connection available (can another request be executed on it) - var isAvailable: Bool { - switch self.state { - case .starting, .closed, .http1(.inUse): - return false - - case .http2(.inUse(let maxStreams, let used)): - return used < maxStreams - - case .http1(.idle), .http2(.idle): - return true - } - } - - /// Is the connection idle and did we create an idle timeout timer for it? - var isParked: Bool { - switch self.state { - case .starting, .closed, .http1(.inUse), .http2(.inUse): - return false - - case .http1(.idle(let parked, _)), .http2(.idle(_, let parked, _)): - return parked - } - } - - /// Is the connection in use (are there requests executing on it) - var isUsed: Bool { - switch self.state { - case .starting, .closed, .http1(.idle), .http2(.idle): - return false - - case .http1(.inUse), .http2(.inUse): - return true - } - } - - var idleSince: NIODeadline? { - switch self.state { - case .starting, .closed, .http1(.inUse), .http2(.inUse): - return nil - - case .http1(.idle(_, let lastIdle)), .http2(.idle(_, _, let lastIdle)): - return lastIdle - } - } - - mutating func http1Started() throws { - guard case .starting = self.state else { - throw Errors.connectionIsNotStarting - } - - self.state = .http1(.idle(parked: false, idleSince: .now())) - } - - mutating func http2Started(maxConcurrentStreams: Int) throws { - guard case .starting = self.state else { - throw Errors.connectionIsNotStarting - } - - self.state = .http2(.idle(maxConcurrentStreams: maxConcurrentStreams, parked: false, lastIdle: .now())) - } - - mutating func park() throws { - switch self.state { - case .starting, .closed, .http1(.inUse), .http2(.inUse): - throw Errors.connectionNotIdle - - case .http1(.idle(true, _)), .http2(.idle(_, true, _)): - throw Errors.connectionIsParked - - case .http1(.idle(false, let lastIdle)): - self.state = .http1(.idle(parked: true, idleSince: lastIdle)) - - case .http2(.idle(let maxStreams, false, let lastIdle)): - self.state = .http2(.idle(maxConcurrentStreams: maxStreams, parked: true, lastIdle: lastIdle)) - } - } - - mutating func activate() throws { - switch self.state { - case .starting, .closed, .http1(.inUse), .http2(.inUse): - throw Errors.connectionNotIdle - - case .http1(.idle(false, _)), .http2(.idle(_, false, _)): - throw Errors.connectionNotParked - - case .http1(.idle(true, let lastIdle)): - self.state = .http1(.idle(parked: false, idleSince: lastIdle)) - - case .http2(.idle(let maxStreams, true, let lastIdle)): - self.state = .http2(.idle(maxConcurrentStreams: maxStreams, parked: false, lastIdle: lastIdle)) - } - } - - mutating func execute(_ request: HTTPSchedulableRequest) throws { - switch self.state { - case .starting, .http1(.inUse): - throw Errors.connectionNotIdle - - case .http1(.idle(true, _)), .http2(.idle(_, true, _)): - throw Errors.connectionIsParked - - case .http1(.idle(false, _)): - if let required = request.requiredEventLoop, required !== self.eventLoop { - throw Errors.connectionDoesNotFulfillEventLoopRequirement - } - self.state = .http1(.inUse) - - case .http2(.idle(let maxStreams, false, _)): - if let required = request.requiredEventLoop, required !== self.eventLoop { - throw Errors.connectionDoesNotFulfillEventLoopRequirement - } - self.state = .http2(.inUse(maxConcurrentStreams: maxStreams, used: 1)) - - case .http2(.inUse(let maxStreams, let used)): - if let required = request.requiredEventLoop, required !== self.eventLoop { - throw Errors.connectionDoesNotFulfillEventLoopRequirement - } - if used >= maxStreams { - throw Errors.connectionDoesNotHaveHTTP2StreamAvailable - } - self.state = .http2(.inUse(maxConcurrentStreams: maxStreams, used: used + 1)) - - case .closed: - throw Errors.connectionIsClosed - } - } - - mutating func finishRequest() throws { - switch self.state { - case .starting, .http1(.idle), .http2(.idle): - throw Errors.connectionIsNotExecuting - - case .http1(.inUse): - self.state = .http1(.idle(parked: false, idleSince: .now())) - - case .http2(.inUse(let maxStreams, let used)): - if used == 1 { - self.state = .http2(.idle(maxConcurrentStreams: maxStreams, parked: false, lastIdle: .now())) - } else { - self.state = .http2(.inUse(maxConcurrentStreams: maxStreams, used: used - 1)) - } - - case .closed: - throw Errors.connectionIsClosed - } - } - - mutating func newHTTP2SettingsReceived(maxConcurrentStreams newMaxStream: Int) throws { - switch self.state { - case .starting: - throw Errors.connectionIsNotActive - - case .http1: - throw Errors.connectionIsNotHTTP2Connection - - case .http2(.inUse(_, let used)): - self.state = .http2(.inUse(maxConcurrentStreams: newMaxStream, used: used)) - - case .http2(.idle(_, let parked, let lastIdle)): - self.state = .http2(.idle(maxConcurrentStreams: newMaxStream, parked: parked, lastIdle: lastIdle)) - - case .closed: - throw Errors.connectionIsClosed - } - } - - mutating func close() throws { - switch self.state { - case .starting: - throw Errors.connectionNotIdle - case .http1(.idle), .http2(.idle): - self.state = .closed - - case .http1(.inUse), .http2(.inUse): - throw Errors.connectionNotIdle - - case .closed: - throw Errors.connectionIsClosed - } - } - } - - private var connections = [Connection.ID: MockConnectionState]() - private var backoff = Set() - - init() {} - - var parked: Int { - self.connections.values.filter { $0.isParked }.count - } - - var used: Int { - self.connections.values.filter { $0.isUsed }.count - } - - var starting: Int { - self.connections.values.filter { $0.isStarting }.count - } - - var count: Int { - self.connections.count - } - - var isEmpty: Bool { - self.connections.isEmpty - } - - var newestParkedConnection: Connection? { - self.connections.values - .filter { $0.isParked } - .max(by: { $0.idleSince! < $1.idleSince! }) - .flatMap { .__testOnly_connection(id: $0.id, eventLoop: $0.eventLoop) } - } - - var oldestParkedConnection: Connection? { - self.connections.values - .filter { $0.isParked } - .min(by: { $0.idleSince! < $1.idleSince! }) - .flatMap { .__testOnly_connection(id: $0.id, eventLoop: $0.eventLoop) } - } - - func newestParkedConnection(for eventLoop: EventLoop) -> Connection? { - self.connections.values - .filter { $0.eventLoop === eventLoop && $0.isParked } - .max(by: { $0.idleSince! < $1.idleSince! }) - .flatMap { .__testOnly_connection(id: $0.id, eventLoop: $0.eventLoop) } - } - - func connections(on eventLoop: EventLoop) -> Int { - self.connections.values.filter { $0.eventLoop === eventLoop }.count - } - - // MARK: Connection creation - - mutating func createConnection(_ connectionID: Connection.ID, on eventLoop: EventLoop) throws { - guard self.connections[connectionID] == nil else { - throw Errors.connectionExists - } - self.connections[connectionID] = .init(id: connectionID, eventLoop: eventLoop) - } - - mutating func succeedConnectionCreationHTTP1(_ connectionID: Connection.ID) throws -> Connection { - guard var connection = self.connections[connectionID] else { - throw Errors.connectionNotFound - } - - try connection.http1Started() - self.connections[connection.id] = connection - return .__testOnly_connection(id: connection.id, eventLoop: connection.eventLoop) - } - - mutating func succeedConnectionCreationHTTP2( - _ connectionID: Connection.ID, - maxConcurrentStreams: Int - ) throws -> Connection { - guard var connection = self.connections[connectionID] else { - throw Errors.connectionNotFound - } - - try connection.http2Started(maxConcurrentStreams: maxConcurrentStreams) - self.connections[connection.id] = connection - return .__testOnly_connection(id: connection.id, eventLoop: connection.eventLoop) - } - - mutating func failConnectionCreation(_ connectionID: Connection.ID) throws { - guard let connection = self.connections[connectionID] else { - throw Errors.connectionNotFound - } - - guard connection.isStarting else { - throw Errors.connectionIsNotStarting - } - - self.connections[connection.id] = nil - } - - mutating func startConnectionBackoffTimer(_ connectionID: Connection.ID) throws { - guard self.connections[connectionID] == nil else { - throw Errors.connectionExists - } - - guard !self.backoff.contains(connectionID) else { - throw Errors.connectionBackoffTimerExists - } - - self.backoff.insert(connectionID) - } - - mutating func newHTTP2ConnectionSettingsReceived( - _ connectionID: Connection.ID, - maxConcurrentStreams: Int - ) throws -> Connection { - guard var connection = self.connections[connectionID] else { - throw Errors.connectionNotFound - } - - try connection.newHTTP2SettingsReceived(maxConcurrentStreams: maxConcurrentStreams) - self.connections[connection.id] = connection - return .__testOnly_connection(id: connection.id, eventLoop: connection.eventLoop) - } - - mutating func connectionBackoffTimerDone(_ connectionID: Connection.ID) throws { - guard self.backoff.remove(connectionID) != nil else { - throw Errors.connectionBackoffTimerNotFound - } - } - - mutating func cancelConnectionBackoffTimer(_ connectionID: Connection.ID) throws { - guard self.backoff.remove(connectionID) != nil else { - throw Errors.connectionBackoffTimerNotFound - } - } - - // MARK: Connection destruction - - /// Closing a connection signals intent. For this reason, it is verified, that the connection is not running any - /// requests when closing. - mutating func closeConnection(_ connection: Connection) throws { - guard var mockConnection = self.connections.removeValue(forKey: connection.id) else { - throw Errors.connectionNotFound - } - - try mockConnection.close() - } - - /// Aborting a connection does not verify if the connection does anything right now - mutating func abortConnection(_ connectionID: Connection.ID) throws { - guard self.connections.removeValue(forKey: connectionID) != nil else { - throw Errors.connectionNotFound - } - } - - // MARK: Connection usage - - mutating func parkConnection(_ connectionID: Connection.ID) throws { - guard var connection = self.connections[connectionID] else { - throw Errors.connectionNotFound - } - - try connection.park() - self.connections[connectionID] = connection - } - - mutating func activateConnection(_ connectionID: Connection.ID) throws { - guard var connection = self.connections[connectionID] else { - throw Errors.connectionNotFound - } - - try connection.activate() - self.connections[connectionID] = connection - } - - mutating func execute(_ request: HTTPSchedulableRequest, on connection: Connection) throws { - guard var connection = self.connections[connection.id] else { - throw Errors.connectionNotFound - } - - try connection.execute(request) - self.connections[connection.id] = connection - } - - mutating func finishExecution(_ connectionID: Connection.ID) throws { - guard var connection = self.connections[connectionID] else { - throw Errors.connectionNotFound - } - - try connection.finishRequest() - self.connections[connectionID] = connection - } -} - -extension MockConnectionPool { - func randomStartingConnection() -> Connection.ID? { - self.connections.values - .filter { $0.isStarting } - .randomElement() - .map(\.id) - } - - func randomActiveConnection() -> Connection.ID? { - self.connections.values - .filter { $0.isUsed || $0.isParked } - .randomElement() - .map(\.id) - } - - func randomParkedConnection() -> Connection? { - self.connections.values - .filter { $0.isParked } - .randomElement() - .flatMap { .__testOnly_connection(id: $0.id, eventLoop: $0.eventLoop) } - } - - func randomLeasedConnection() -> Connection? { - self.connections.values - .filter { $0.isUsed } - .randomElement() - .flatMap { .__testOnly_connection(id: $0.id, eventLoop: $0.eventLoop) } - } - - func randomBackingOffConnection() -> Connection.ID? { - self.backoff.randomElement() - } - - mutating func closeRandomActiveConnection() -> Connection.ID? { - guard let connectionID = self.randomActiveConnection() else { - return nil - } - - self.connections.removeValue(forKey: connectionID) - return connectionID - } - - enum SetupError: Error { - case totalNumberOfConnectionsMustBeLowerThanIdle - case expectedConnectionToBeCreated - case expectedRequestToBeAddedToQueue - case expectedPreviouslyQueuedRequestToBeRunNow - case expectedNoConnectionAction - case expectedConnectionToBeParked - } - - static func http1( - elg: EventLoopGroup, - on eventLoop: EventLoop? = nil, - numberOfConnections: Int, - maxNumberOfConnections: Int = 8 - ) throws -> (Self, HTTPConnectionPool.StateMachine) { - var state = HTTPConnectionPool.StateMachine( - idGenerator: .init(), - maximumConcurrentHTTP1Connections: maxNumberOfConnections, - retryConnectionEstablishment: true, - preferHTTP1: true, - maximumConnectionUses: nil - ) - var connections = MockConnectionPool() - var queuer = MockRequestQueuer() - - for _ in 0.. (Self, HTTPConnectionPool.StateMachine) { - var state = HTTPConnectionPool.StateMachine( - idGenerator: .init(), - maximumConcurrentHTTP1Connections: 8, - retryConnectionEstablishment: true, - preferHTTP1: false, - maximumConnectionUses: nil - ) - var connections = MockConnectionPool() - var queuer = MockRequestQueuer() - - // 1. Schedule one request to create a connection - - let mockRequest = MockHTTPScheduableRequest(eventLoop: eventLoop ?? elg.next()) - let request = HTTPConnectionPool.Request(mockRequest) - let executeAction = state.executeRequest(request) - - guard case .scheduleRequestTimeout(request, on: let waitEL) = executeAction.request, - mockRequest.eventLoop === waitEL - else { - throw SetupError.expectedRequestToBeAddedToQueue - } - - guard case .createConnection(let connectionID, on: let eventLoop) = executeAction.connection else { - throw SetupError.expectedConnectionToBeCreated - } - - try connections.createConnection(connectionID, on: eventLoop) - try queuer.queue(mockRequest, id: request.id) - - // 2. the connection becomes available - - let newConnection = try connections.succeedConnectionCreationHTTP2( - connectionID, - maxConcurrentStreams: maxConcurrentStreams - ) - let action = state.newHTTP2ConnectionCreated(newConnection, maxConcurrentStreams: maxConcurrentStreams) - - guard case .executeRequestsAndCancelTimeouts([request], newConnection) = action.request else { - throw SetupError.expectedPreviouslyQueuedRequestToBeRunNow - } - - guard try queuer.get(request.id, request: request.__testOnly_wrapped_request()) === mockRequest else { - throw SetupError.expectedPreviouslyQueuedRequestToBeRunNow - } - try connections.execute(mockRequest, on: newConnection) - - // 3. park connection - - try connections.finishExecution(newConnection.id) - - let expected: HTTPConnectionPool.StateMachine.ConnectionAction = .scheduleTimeoutTimer( - newConnection.id, - on: newConnection.eventLoop - ) - guard state.http2ConnectionStreamClosed(newConnection.id) == .init(request: .none, connection: expected) else { - throw SetupError.expectedConnectionToBeParked - } - - try connections.parkConnection(newConnection.id) - - return (connections, state) - } -} - -/// A request that can be used when testing the `HTTPConnectionPool.StateMachine` -/// with the `MockConnectionPool`. -final class MockHTTPScheduableRequest: HTTPSchedulableRequest { - let logger: Logger - let connectionDeadline: NIODeadline - let requestOptions: RequestOptions - - let preferredEventLoop: EventLoop - let requiredEventLoop: EventLoop? - - init( - eventLoop: EventLoop, - logger: Logger = Logger(label: "mock"), - connectionTimeout: TimeAmount = .seconds(60), - requiresEventLoopForChannel: Bool = false - ) { - self.logger = logger - - self.connectionDeadline = .now() + connectionTimeout - self.requestOptions = .forTests() - - self.preferredEventLoop = eventLoop - if requiresEventLoopForChannel { - self.requiredEventLoop = eventLoop - } else { - self.requiredEventLoop = nil - } - } - - var eventLoop: EventLoop { - self.preferredEventLoop - } - - // MARK: HTTPSchedulableRequest - - var poolKey: ConnectionPool.Key { - preconditionFailure("Unimplemented") - } - - var tlsConfiguration: TLSConfiguration? { nil } - - func requestWasQueued(_: HTTPRequestScheduler) { - preconditionFailure("Unimplemented") - } - - func fail(_: Error) { - preconditionFailure("Unimplemented") - } - - // MARK: HTTPExecutableRequest - - var requestHead: HTTPRequestHead { - preconditionFailure("Unimplemented") - } - - var requestFramingMetadata: RequestFramingMetadata { - preconditionFailure("Unimplemented") - } - - func willExecuteRequest(_: HTTPRequestExecutor) { - preconditionFailure("Unimplemented") - } - - func requestHeadSent() { - preconditionFailure("Unimplemented") - } - - func resumeRequestBodyStream() { - preconditionFailure("Unimplemented") - } - - func pauseRequestBodyStream() { - preconditionFailure("Unimplemented") - } - - func receiveResponseHead(_: HTTPResponseHead) { - preconditionFailure("Unimplemented") - } - - func receiveResponseBodyParts(_: CircularBuffer) { - preconditionFailure("Unimplemented") - } - - func succeedRequest(_: CircularBuffer?) { - preconditionFailure("Unimplemented") - } -} diff --git a/Tests/AsyncHTTPClientTests/Mocks/MockHTTPExecutableRequest.swift b/Tests/AsyncHTTPClientTests/Mocks/MockHTTPExecutableRequest.swift deleted file mode 100644 index 67f18cbb8..000000000 --- a/Tests/AsyncHTTPClientTests/Mocks/MockHTTPExecutableRequest.swift +++ /dev/null @@ -1,175 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOHTTP1 -import XCTest - -@testable import AsyncHTTPClient - -final class MockHTTPExecutableRequest: HTTPExecutableRequest { - enum Event: Sendable { - /// ``Event`` without associated values - enum Kind: Hashable { - case willExecuteRequest - case requestHeadSent - case resumeRequestBodyStream - case pauseRequestBodyStream - case receiveResponseHead - case receiveResponseBodyParts - case succeedRequest - case fail - } - - case willExecuteRequest(HTTPRequestExecutor) - case requestHeadSent - case resumeRequestBodyStream - case pauseRequestBodyStream - case receiveResponseHead(HTTPResponseHead) - case receiveResponseBodyParts(CircularBuffer) - case succeedRequest(CircularBuffer?) - case fail(Error) - - var kind: Kind { - switch self { - case .willExecuteRequest: return .willExecuteRequest - case .requestHeadSent: return .requestHeadSent - case .resumeRequestBodyStream: return .resumeRequestBodyStream - case .pauseRequestBodyStream: return .pauseRequestBodyStream - case .receiveResponseHead: return .receiveResponseHead - case .receiveResponseBodyParts: return .receiveResponseBodyParts - case .succeedRequest: return .succeedRequest - case .fail: return .fail - } - } - } - - let logger: Logging.Logger = Logger(label: "request") - let requestHead: NIOHTTP1.HTTPRequestHead - let requestFramingMetadata: RequestFramingMetadata - let requestOptions: RequestOptions = .forTests() - - /// if true and ``HTTPExecutableRequest`` method is called without setting a corresponding callback on `self` e.g. - /// If ``HTTPExecutableRequest\.willExecuteRequest(_:)`` is called but ``willExecuteRequestCallback`` is not set, - /// ``XCTestFail(_:)`` will be called to fail the current test. - let raiseErrorIfUnimplementedMethodIsCalled: Bool - private let file: StaticString - private let line: UInt - - let willExecuteRequestCallback: (@Sendable (HTTPRequestExecutor) -> Void)? = nil - let requestHeadSentCallback: (@Sendable () -> Void)? = nil - let resumeRequestBodyStreamCallback: (@Sendable () -> Void)? = nil - let pauseRequestBodyStreamCallback: (@Sendable () -> Void)? = nil - let receiveResponseHeadCallback: (@Sendable (HTTPResponseHead) -> Void)? = nil - let receiveResponseBodyPartsCallback: (@Sendable (CircularBuffer) -> Void)? = nil - let succeedRequestCallback: (@Sendable (CircularBuffer?) -> Void)? = nil - let failCallback: (@Sendable (Error) -> Void)? = nil - - /// captures all ``HTTPExecutableRequest`` method calls in the order of occurrence, including arguments. - /// If you are not interested in the arguments you can use `events.map(\.kind)` to get all events without arguments. - private let _events = NIOLockedValueBox<[Event]>([]) - private(set) var events: [Event] { - get { - self._events.withLockedValue { $0 } - } - set { - self._events.withLockedValue { $0 = newValue } - } - } - - init( - head: NIOHTTP1.HTTPRequestHead = .init(version: .http1_1, method: .GET, uri: "http://localhost/"), - framingMetadata: RequestFramingMetadata = .init(connectionClose: false, body: .fixedSize(0)), - raiseErrorIfUnimplementedMethodIsCalled: Bool = true, - file: StaticString = #file, - line: UInt = #line - ) { - self.requestHead = head - self.requestFramingMetadata = framingMetadata - self.raiseErrorIfUnimplementedMethodIsCalled = raiseErrorIfUnimplementedMethodIsCalled - self.file = file - self.line = line - } - - private func calledUnimplementedMethod(_ name: String) { - guard self.raiseErrorIfUnimplementedMethodIsCalled else { return } - XCTFail("\(name) invoked but it is not implemented", file: self.file, line: self.line) - } - - func willExecuteRequest(_ executor: HTTPRequestExecutor) { - self.events.append(.willExecuteRequest(executor)) - guard let willExecuteRequestCallback = willExecuteRequestCallback else { - return self.calledUnimplementedMethod(#function) - } - willExecuteRequestCallback(executor) - } - - func requestHeadSent() { - self.events.append(.requestHeadSent) - guard let requestHeadSentCallback = requestHeadSentCallback else { - return self.calledUnimplementedMethod(#function) - } - requestHeadSentCallback() - } - - func resumeRequestBodyStream() { - self.events.append(.resumeRequestBodyStream) - guard let resumeRequestBodyStreamCallback = resumeRequestBodyStreamCallback else { - return self.calledUnimplementedMethod(#function) - } - resumeRequestBodyStreamCallback() - } - - func pauseRequestBodyStream() { - self.events.append(.pauseRequestBodyStream) - guard let pauseRequestBodyStreamCallback = pauseRequestBodyStreamCallback else { - return self.calledUnimplementedMethod(#function) - } - pauseRequestBodyStreamCallback() - } - - func receiveResponseHead(_ head: HTTPResponseHead) { - self.events.append(.receiveResponseHead(head)) - guard let receiveResponseHeadCallback = receiveResponseHeadCallback else { - return self.calledUnimplementedMethod(#function) - } - receiveResponseHeadCallback(head) - } - - func receiveResponseBodyParts(_ buffer: CircularBuffer) { - self.events.append(.receiveResponseBodyParts(buffer)) - guard let receiveResponseBodyPartsCallback = receiveResponseBodyPartsCallback else { - return self.calledUnimplementedMethod(#function) - } - receiveResponseBodyPartsCallback(buffer) - } - - func succeedRequest(_ buffer: CircularBuffer?) { - self.events.append(.succeedRequest(buffer)) - guard let succeedRequestCallback = succeedRequestCallback else { - return self.calledUnimplementedMethod(#function) - } - succeedRequestCallback(buffer) - } - - func fail(_ error: Error) { - self.events.append(.fail(error)) - guard let failCallback = failCallback else { - return self.calledUnimplementedMethod(#function) - } - failCallback(error) - } -} diff --git a/Tests/AsyncHTTPClientTests/Mocks/MockRequestExecutor.swift b/Tests/AsyncHTTPClientTests/Mocks/MockRequestExecutor.swift deleted file mode 100644 index e5d9caa8e..000000000 --- a/Tests/AsyncHTTPClientTests/Mocks/MockRequestExecutor.swift +++ /dev/null @@ -1,308 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOConcurrencyHelpers -import NIOCore - -@testable import AsyncHTTPClient - -// This is a MockRequestExecutor, that is synchronized on its EventLoop. -final class MockRequestExecutor { - enum Errors: Error { - case eof - case unexpectedFileRegion - case unexpectedByteBuffer - } - - enum RequestParts: Equatable, Sendable { - case body(IOData) - case endOfStream - - var isBody: Bool { - switch self { - case .body: - return true - case .endOfStream: - return false - } - } - } - - var isCancelled: Bool { - self.cancellationLock.value - } - - var signalledDemandForResponseBody: Bool { - self.responseBodyDemandLock.value - } - - var requestBodyPartsCount: Int { - self.blockingQueue.count - } - - let eventLoop: EventLoop - let pauseRequestBodyPartStreamAfterASingleWrite: Bool - - private let blockingQueue = BlockingQueue() - private let responseBodyDemandLock = ConditionLock(value: false) - private let cancellationLock = ConditionLock(value: false) - - private struct State: Sendable { - var request: HTTPExecutableRequest? - var _signaledDemandForRequestBody: Bool = false - } - - private let state: NIOLockedValueBox - - init(pauseRequestBodyPartStreamAfterASingleWrite: Bool = false, eventLoop: EventLoop) { - self.state = NIOLockedValueBox(State()) - self.pauseRequestBodyPartStreamAfterASingleWrite = pauseRequestBodyPartStreamAfterASingleWrite - self.eventLoop = eventLoop - } - - func runRequest(_ request: HTTPExecutableRequest) { - if self.eventLoop.inEventLoop { - self.runRequest0(request) - } else { - self.eventLoop.execute { - self.runRequest0(request) - } - } - } - - private func runRequest0(_ request: HTTPExecutableRequest) { - self.state.withLockedValue { - precondition($0.request == nil) - $0.request = request - } - request.willExecuteRequest(self) - request.requestHeadSent() - } - - func receiveRequestBody(deadline: NIODeadline = .now() + .seconds(5), _ verify: (ByteBuffer) throws -> Void) throws - { - enum ReceiveAction { - case value(RequestParts) - case future(EventLoopFuture) - } - - switch try self.blockingQueue.popFirst(deadline: deadline) { - case .body(.byteBuffer(let buffer)): - try verify(buffer) - case .body(.fileRegion): - throw Errors.unexpectedFileRegion - case .endOfStream: - throw Errors.eof - } - } - - func receiveEndOfStream(deadline: NIODeadline = .now() + .seconds(5)) throws { - enum ReceiveAction { - case value(RequestParts) - case future(EventLoopFuture) - } - - switch try self.blockingQueue.popFirst(deadline: deadline) { - case .body(.byteBuffer): - throw Errors.unexpectedByteBuffer - case .body(.fileRegion): - throw Errors.unexpectedFileRegion - case .endOfStream: - break - } - } - - func pauseRequestBodyStream() { - if self.eventLoop.inEventLoop { - self.pauseRequestBodyStream0() - } else { - self.eventLoop.execute { - self.pauseRequestBodyStream0() - } - } - } - - private func pauseRequestBodyStream0() { - let request = self.state.withLockedValue { - if $0._signaledDemandForRequestBody == true { - $0._signaledDemandForRequestBody = false - return $0.request - } else { - return nil - } - } - - request?.pauseRequestBodyStream() - } - - func resumeRequestBodyStream() { - if self.eventLoop.inEventLoop { - self.resumeRequestBodyStream0() - } else { - self.eventLoop.execute { - self.resumeRequestBodyStream0() - } - } - } - - private func resumeRequestBodyStream0() { - let request = self.state.withLockedValue { - if $0._signaledDemandForRequestBody == false { - $0._signaledDemandForRequestBody = true - return $0.request - } else { - return nil - } - } - - request?.resumeRequestBodyStream() - } - - func resetResponseStreamDemandSignal() { - self.responseBodyDemandLock.lock() - self.responseBodyDemandLock.unlock(withValue: false) - } - - func receiveResponseDemand(deadline: NIODeadline = .now() + .seconds(5)) throws { - let secondsUntilDeath = deadline - NIODeadline.now() - guard - self.responseBodyDemandLock.lock( - whenValue: true, - timeoutSeconds: .init(secondsUntilDeath.nanoseconds / 1_000_000_000) - ) - else { - throw TimeoutError() - } - - self.responseBodyDemandLock.unlock() - } - - func receiveCancellation(deadline: NIODeadline = .now() + .seconds(5)) throws { - let secondsUntilDeath = deadline - NIODeadline.now() - guard - self.cancellationLock.lock( - whenValue: true, - timeoutSeconds: .init(secondsUntilDeath.nanoseconds / 1_000_000_000) - ) - else { - throw TimeoutError() - } - - self.cancellationLock.unlock() - } -} - -extension MockRequestExecutor: HTTPRequestExecutor { - // this should always be called twice. When we receive the first call, the next call to produce - // data is already scheduled. If we call pause here, once, after the second call new subsequent - // calls should not be scheduled. - func writeRequestBodyPart(_ part: IOData, request: HTTPExecutableRequest, promise: EventLoopPromise?) { - self.writeNextRequestPart(.body(part), request: request) - promise?.succeed(()) - } - - func finishRequestBodyStream(_ request: HTTPExecutableRequest, promise: EventLoopPromise?) { - self.writeNextRequestPart(.endOfStream, request: request) - promise?.succeed(()) - } - - private func writeNextRequestPart(_ part: RequestParts, request: HTTPExecutableRequest) { - enum WriteAction { - case pauseBodyStream - case none - } - - let stateChange = { @Sendable () -> WriteAction in - var pause = false - if self.blockingQueue.isEmpty && self.pauseRequestBodyPartStreamAfterASingleWrite && part.isBody { - pause = true - self.state.withLockedValue { - $0._signaledDemandForRequestBody = false - } - } - - self.blockingQueue.append(.success(part)) - - return pause ? .pauseBodyStream : .none - } - - let action: WriteAction - if self.eventLoop.inEventLoop { - action = stateChange() - } else { - action = try! self.eventLoop.submit(stateChange).wait() - } - - switch action { - case .pauseBodyStream: - request.pauseRequestBodyStream() - case .none: - return - } - } - - func demandResponseBodyStream(_: HTTPExecutableRequest) { - self.responseBodyDemandLock.lock() - self.responseBodyDemandLock.unlock(withValue: true) - } - - func cancelRequest(_: HTTPExecutableRequest) { - self.cancellationLock.lock() - self.cancellationLock.unlock(withValue: true) - } -} - -extension MockRequestExecutor { - public struct TimeoutError: Error {} - - final class BlockingQueue { - private let condition = ConditionLock(value: false) - private var buffer = CircularBuffer>() - - internal func append(_ element: Result) { - self.condition.lock() - self.buffer.append(element) - self.condition.unlock(withValue: true) - } - - internal var isEmpty: Bool { - self.condition.lock() - defer { self.condition.unlock() } - return self.buffer.isEmpty - } - - internal var count: Int { - self.condition.lock() - defer { self.condition.unlock() } - return self.buffer.count - } - - internal func popFirst(deadline: NIODeadline) throws -> Element { - let secondsUntilDeath = deadline - NIODeadline.now() - guard - self.condition.lock( - whenValue: true, - timeoutSeconds: .init(secondsUntilDeath.nanoseconds / 1_000_000_000) - ) - else { - throw TimeoutError() - } - let first = self.buffer.removeFirst() - self.condition.unlock(withValue: !self.buffer.isEmpty) - return try first.get() - } - } -} - -extension MockRequestExecutor.BlockingQueue: @unchecked Sendable where Element: Sendable {} diff --git a/Tests/AsyncHTTPClientTests/Mocks/MockRequestQueuer.swift b/Tests/AsyncHTTPClientTests/Mocks/MockRequestQueuer.swift deleted file mode 100644 index 44e820444..000000000 --- a/Tests/AsyncHTTPClientTests/Mocks/MockRequestQueuer.swift +++ /dev/null @@ -1,93 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOCore -import NIOHTTP1 - -@testable import AsyncHTTPClient - -/// A mock request queue (not creating any timers) that is used to validate -/// request actions returned by the `HTTPConnectionPool.StateMachine`. -struct MockRequestQueuer { - enum Errors: Error { - case requestIDNotFound - case requestIDAlreadyUsed - case requestIDDoesNotMatchTask - } - - typealias RequestID = HTTPConnectionPool.Request.ID - - private struct QueuedRequest { - let id: RequestID - let request: HTTPSchedulableRequest - } - - init() { - self.waiters = [:] - } - - private var waiters: [RequestID: QueuedRequest] - - var count: Int { - self.waiters.count - } - - var isEmpty: Bool { - self.waiters.isEmpty - } - - mutating func queue(_ request: HTTPSchedulableRequest, id: RequestID) throws { - guard self.waiters[id] == nil else { - throw Errors.requestIDAlreadyUsed - } - - self.waiters[id] = QueuedRequest(id: id, request: request) - } - - mutating func fail(_ id: RequestID, request: HTTPSchedulableRequest) throws { - guard let waiter = self.waiters.removeValue(forKey: id) else { - throw Errors.requestIDNotFound - } - guard waiter.request === request else { - throw Errors.requestIDDoesNotMatchTask - } - } - - mutating func get(_ id: RequestID, request: HTTPSchedulableRequest) throws -> HTTPSchedulableRequest { - guard let waiter = self.waiters.removeValue(forKey: id) else { - throw Errors.requestIDNotFound - } - guard waiter.request === request else { - throw Errors.requestIDDoesNotMatchTask - } - return waiter.request - } - - @discardableResult - mutating func cancel(_ id: RequestID) throws -> HTTPSchedulableRequest { - guard let waiter = self.waiters.removeValue(forKey: id) else { - throw Errors.requestIDNotFound - } - return waiter.request - } - - mutating func timeoutRandomRequest() -> (RequestID, HTTPSchedulableRequest)? { - guard let waiter = self.waiters.randomElement() else { - return nil - } - self.waiters.removeValue(forKey: waiter.key) - return (waiter.key, waiter.value.request) - } -} diff --git a/Tests/AsyncHTTPClientTests/NWWaitingHandlerTests.swift b/Tests/AsyncHTTPClientTests/NWWaitingHandlerTests.swift deleted file mode 100644 index 63eaf649d..000000000 --- a/Tests/AsyncHTTPClientTests/NWWaitingHandlerTests.swift +++ /dev/null @@ -1,108 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2023 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -#if canImport(Network) -@testable import AsyncHTTPClient -import Network -import NIOCore -import NIOConcurrencyHelpers -import NIOEmbedded -import NIOSSL -import NIOTransportServices -import XCTest - -@available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 5.0, *) -class NWWaitingHandlerTests: XCTestCase { - final class MockRequester: HTTPConnectionRequester { - private struct State: Sendable { - var waitingForConnectivityCalled = false - var connectionID: AsyncHTTPClient.HTTPConnectionPool.Connection.ID? - var transientError: NWError? - } - - private let state = NIOLockedValueBox(State()) - - var waitingForConnectivityCalled: Bool { - self.state.withLockedValue { $0.waitingForConnectivityCalled } - } - - var connectionID: AsyncHTTPClient.HTTPConnectionPool.Connection.ID? { - self.state.withLockedValue { $0.connectionID } - } - - var transientError: NWError? { - self.state.withLockedValue { - $0.transientError - } - } - - func http1ConnectionCreated(_: AsyncHTTPClient.HTTP1Connection.SendableView) {} - - func http2ConnectionCreated(_: AsyncHTTPClient.HTTP2Connection.SendableView, maximumStreams: Int) {} - - func failedToCreateHTTPConnection(_: AsyncHTTPClient.HTTPConnectionPool.Connection.ID, error: Error) {} - - func waitingForConnectivity(_ connectionID: AsyncHTTPClient.HTTPConnectionPool.Connection.ID, error: Error) { - self.state.withLockedValue { - $0.waitingForConnectivityCalled = true - $0.connectionID = connectionID - $0.transientError = error as? NWError - } - } - } - - func testWaitingHandlerInvokesWaitingForConnectivity() { - let requester = MockRequester() - let connectionID: AsyncHTTPClient.HTTPConnectionPool.Connection.ID = 1 - let waitingEventHandler = NWWaitingHandler(requester: requester, connectionID: connectionID) - let embedded = EmbeddedChannel(handlers: [waitingEventHandler]) - - embedded.pipeline.fireUserInboundEventTriggered( - NIOTSNetworkEvents.WaitingForConnectivity(transientError: .dns(1)) - ) - - XCTAssertTrue( - requester.waitingForConnectivityCalled, - "Expected the handler to invoke .waitingForConnectivity on the requester" - ) - XCTAssertEqual(requester.connectionID, connectionID, "Expected the handler to pass connectionID to requester") - XCTAssertEqual(requester.transientError, NWError.dns(1)) - } - - func testWaitingHandlerDoesNotInvokeWaitingForConnectionOnUnrelatedErrors() { - let requester = MockRequester() - let waitingEventHandler = NWWaitingHandler(requester: requester, connectionID: 1) - let embedded = EmbeddedChannel(handlers: [waitingEventHandler]) - embedded.pipeline.fireUserInboundEventTriggered(NIOTSNetworkEvents.BetterPathAvailable()) - - XCTAssertFalse( - requester.waitingForConnectivityCalled, - "Should not call .waitingForConnectivity on unrelated events" - ) - } - - func testWaitingHandlerPassesTheEventDownTheContext() { - let requester = MockRequester() - let waitingEventHandler = NWWaitingHandler(requester: requester, connectionID: 1) - let tlsEventsHandler = TLSEventsHandler(deadline: nil) - let embedded = EmbeddedChannel(handlers: [waitingEventHandler, tlsEventsHandler]) - - embedded.pipeline.fireErrorCaught(NIOSSLError.handshakeFailed(BoringSSLError.wantConnect)) - XCTAssertThrowsError(try XCTUnwrap(tlsEventsHandler.tlsEstablishedFuture).wait()) { - XCTAssertEqualTypeAndValue($0, NIOSSLError.handshakeFailed(BoringSSLError.wantConnect)) - } - } -} - -#endif diff --git a/Tests/AsyncHTTPClientTests/NoBytesSentOverBodyLimitTests.swift b/Tests/AsyncHTTPClientTests/NoBytesSentOverBodyLimitTests.swift deleted file mode 100644 index 026a45d4c..000000000 --- a/Tests/AsyncHTTPClientTests/NoBytesSentOverBodyLimitTests.swift +++ /dev/null @@ -1,81 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2022 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import AsyncHTTPClient -import Atomics -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOFoundationCompat -import NIOHTTP1 -import NIOHTTPCompression -import NIOPosix -import NIOSSL -import NIOTestUtils -import NIOTransportServices -import XCTest - -#if canImport(Network) -import Network -#endif - -final class NoBytesSentOverBodyLimitTests: XCTestCaseHTTPClientTestsBaseClass { - func testNoBytesSentOverBodyLimit() throws { - let server = NIOHTTP1TestServer(group: self.serverGroup) - defer { - XCTAssertNoThrow(try server.stop()) - } - - let tooLong = "XBAD BAD BAD NOT HTTP/1.1\r\n\r\n" - - let request = try Request( - url: "http://localhost/(server.serverPort)", - body: .stream(contentLength: 1) { streamWriter in - streamWriter.write(.byteBuffer(ByteBuffer(string: tooLong))) - } - ) - - let future = self.defaultClient.execute(request: request) - - // Okay, what happens here needs an explanation: - // - // In the request state machine, we should start the request, which will lead to an - // invocation of `context.write(HTTPRequestHead)`. Since we will receive a streamed request - // body a `context.flush()` will be issued. Further the request stream will be started. - // Since the request stream immediately produces to much data, the request will be failed - // and the connection will be closed. - // - // Even though a flush was issued after the request head, there is no guarantee that the - // request head was written to the network. For this reason we must accept not receiving a - // request and receiving a request head. - - do { - _ = try server.receiveHead() - - // A request head was sent. We expect the request now to fail with a parsing error, - // since the client ended the connection to early (from the server's point of view.) - XCTAssertThrowsError(try server.readInbound()) { - XCTAssertEqual($0 as? HTTPParserError, HTTPParserError.invalidEOFState) - } - } catch { - // TBD: We sadly can't verify the error type, since it is private in `NIOTestUtils`: - // NIOTestUtils.BlockingQueue.TimeoutError - } - - // request must always be failed with this error - XCTAssertThrowsError(try future.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .bodyLengthMismatch) - } - } -} diff --git a/Tests/AsyncHTTPClientTests/RacePoolIdleConnectionsAndGetTests.swift b/Tests/AsyncHTTPClientTests/RacePoolIdleConnectionsAndGetTests.swift deleted file mode 100644 index 35a09c421..000000000 --- a/Tests/AsyncHTTPClientTests/RacePoolIdleConnectionsAndGetTests.swift +++ /dev/null @@ -1,47 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2022 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import AsyncHTTPClient -import Atomics -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOFoundationCompat -import NIOHTTP1 -import NIOHTTPCompression -import NIOPosix -import NIOSSL -import NIOTestUtils -import NIOTransportServices -import XCTest - -#if canImport(Network) -import Network -#endif - -final class RacePoolIdleConnectionsAndGetTests: XCTestCaseHTTPClientTestsBaseClass { - func testRacePoolIdleConnectionsAndGet() { - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: .init(connectionPool: .init(idleTimeout: .milliseconds(10))) - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - } - for _ in 1...200 { - XCTAssertNoThrow(try localClient.get(url: self.defaultHTTPBinURLPrefix + "get").wait()) - Thread.sleep(forTimeInterval: 0.01 + .random(in: -0.01...0.01)) - } - } -} diff --git a/Tests/AsyncHTTPClientTests/RequestBagTests.swift b/Tests/AsyncHTTPClientTests/RequestBagTests.swift deleted file mode 100644 index 2b0c2f6e4..000000000 --- a/Tests/AsyncHTTPClientTests/RequestBagTests.swift +++ /dev/null @@ -1,1122 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Atomics -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOEmbedded -import NIOHTTP1 -import NIOPosix -import XCTest - -@testable import AsyncHTTPClient - -final class RequestBagTests: XCTestCase { - func testWriteBackpressureWorks() { - let embeddedEventLoop = EmbeddedEventLoop() - defer { XCTAssertNoThrow(try embeddedEventLoop.syncShutdownGracefully()) } - let logger = Logger(label: "test") - - struct TestState { - var writtenBytes: Int = 0 - var writes: Int = 0 - var streamIsAllowedToWrite: Bool = false - } - - let testState = NIOLockedValueBox(TestState()) - - let bytesToSent = (3000...10000).randomElement()! - let expectedWrites = bytesToSent / 100 + ((bytesToSent % 100 > 0) ? 1 : 0) - - let writeDonePromise = embeddedEventLoop.makePromise(of: Void.self) - let requestBody: HTTPClient.Body = .stream(contentLength: Int64(bytesToSent)) { - writer -> EventLoopFuture in - @Sendable func write(donePromise: EventLoopPromise) { - let futureWrite: EventLoopFuture? = testState.withLockedValue { state in - XCTAssertTrue(state.streamIsAllowedToWrite) - guard state.writtenBytes < bytesToSent else { - donePromise.succeed(()) - return nil - } - let byteCount = min(bytesToSent - state.writtenBytes, 100) - let buffer = ByteBuffer(bytes: [UInt8](repeating: 1, count: byteCount)) - state.writes += 1 - return writer.write(.byteBuffer(buffer)) - } - - futureWrite?.whenSuccess { _ in - testState.withLockedValue { state in - state.writtenBytes += 100 - } - write(donePromise: donePromise) - } - } - - write(donePromise: writeDonePromise) - - return writeDonePromise.futureResult - } - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow( - maybeRequest = try HTTPClient.Request(url: "https://swift.org/", method: .POST, body: requestBody) - ) - guard let request = maybeRequest else { return XCTFail("Expected to have a request") } - let delegate = UploadCountingDelegate(eventLoop: embeddedEventLoop) - - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embeddedEventLoop), - task: .init(eventLoop: embeddedEventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let bag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag.") } - - XCTAssert(bag.task.eventLoop === embeddedEventLoop) - - let executor = MockRequestExecutor( - pauseRequestBodyPartStreamAfterASingleWrite: true, - eventLoop: embeddedEventLoop - ) - - XCTAssertEqual(delegate.hitDidSendRequestHead, 0) - executor.runRequest(bag) - XCTAssertEqual(delegate.hitDidSendRequestHead, 1) - - testState.withLockedValue { $0.streamIsAllowedToWrite = true } - bag.resumeRequestBodyStream() - testState.withLockedValue { $0.streamIsAllowedToWrite = false } - - // after starting the body stream we should have received two writes - var receivedBytes = 0 - for i in 0.. EventLoopFuture in - - writer.write(.byteBuffer(ByteBuffer(bytes: 0...3))).flatMap { _ -> EventLoopFuture in - embeddedEventLoop.makeFailedFuture(TestError()) - } - } - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow( - maybeRequest = try HTTPClient.Request(url: "https://swift.org/", method: .POST, body: requestBody) - ) - guard let request = maybeRequest else { return XCTFail("Expected to have a request") } - - let delegate = UploadCountingDelegate(eventLoop: embeddedEventLoop) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embeddedEventLoop), - task: .init(eventLoop: embeddedEventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let bag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag.") } - XCTAssert(bag.task.eventLoop === embeddedEventLoop) - - let executor = MockRequestExecutor(eventLoop: embeddedEventLoop) - - XCTAssertEqual(delegate.hitDidSendRequestHead, 0) - executor.runRequest(bag) - XCTAssertEqual(delegate.hitDidSendRequestHead, 1) - XCTAssertEqual(delegate.hitDidSendRequestPart, 0) - bag.resumeRequestBodyStream() - XCTAssertEqual(delegate.hitDidSendRequestPart, 1) - XCTAssertEqual(delegate.hitDidReceiveError, 1) - XCTAssertEqual(delegate.lastError as? TestError, TestError()) - - XCTAssertTrue(executor.isCancelled) - - XCTAssertThrowsError(try bag.task.futureResult.wait()) { - XCTAssertEqual($0 as? TestError, TestError()) - } - } - - func testCancelFailsTaskBeforeRequestIsSent() { - let embeddedEventLoop = EmbeddedEventLoop() - defer { XCTAssertNoThrow(try embeddedEventLoop.syncShutdownGracefully()) } - let logger = Logger(label: "test") - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://swift.org/")) - guard let request = maybeRequest else { return XCTFail("Expected to have a request") } - - let delegate = UploadCountingDelegate(eventLoop: embeddedEventLoop) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embeddedEventLoop), - task: .init(eventLoop: embeddedEventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let bag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag.") } - XCTAssert(bag.eventLoop === embeddedEventLoop) - - let executor = MockRequestExecutor(eventLoop: embeddedEventLoop) - bag.fail(HTTPClientError.cancelled) - - bag.willExecuteRequest(executor) - XCTAssertTrue(executor.isCancelled, "The request bag, should call cancel immediately on the executor") - XCTAssertThrowsError(try bag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .cancelled) - } - } - - func testDeadlineExceededFailsTaskEvenIfRaceBetweenCancelingSchedulerAndRequestStart() { - let embeddedEventLoop = EmbeddedEventLoop() - defer { XCTAssertNoThrow(try embeddedEventLoop.syncShutdownGracefully()) } - let logger = Logger(label: "test") - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://swift.org/")) - guard let request = maybeRequest else { return XCTFail("Expected to have a request") } - - let delegate = UploadCountingDelegate(eventLoop: embeddedEventLoop) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embeddedEventLoop), - task: .init(eventLoop: embeddedEventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let bag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag.") } - XCTAssert(bag.eventLoop === embeddedEventLoop) - - let queuer = MockTaskQueuer() - bag.requestWasQueued(queuer) - - let executor = MockRequestExecutor(eventLoop: embeddedEventLoop) - XCTAssertEqual(queuer.hitCancelCount, 0) - bag.deadlineExceeded() - XCTAssertEqual(queuer.hitCancelCount, 1) - - bag.willExecuteRequest(executor) - XCTAssertTrue(executor.isCancelled, "The request bag, should call cancel immediately on the executor") - XCTAssertThrowsError(try bag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .deadlineExceeded) - } - } - - func testCancelHasNoEffectAfterDeadlineExceededFailsTask() { - struct MyError: Error, Equatable {} - let embeddedEventLoop = EmbeddedEventLoop() - defer { XCTAssertNoThrow(try embeddedEventLoop.syncShutdownGracefully()) } - let logger = Logger(label: "test") - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://swift.org/")) - guard let request = maybeRequest else { return XCTFail("Expected to have a request") } - - let delegate = UploadCountingDelegate(eventLoop: embeddedEventLoop) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embeddedEventLoop), - task: .init(eventLoop: embeddedEventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let bag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag.") } - XCTAssert(bag.eventLoop === embeddedEventLoop) - - let queuer = MockTaskQueuer() - bag.requestWasQueued(queuer) - - XCTAssertEqual(queuer.hitCancelCount, 0) - bag.deadlineExceeded() - XCTAssertEqual(queuer.hitCancelCount, 1) - XCTAssertEqual(delegate.hitDidReceiveError, 0) - bag.fail(MyError()) - XCTAssertEqual(delegate.hitDidReceiveError, 1) - - bag.fail(HTTPClientError.cancelled) - XCTAssertEqual(delegate.hitDidReceiveError, 1) - - XCTAssertThrowsError(try bag.task.futureResult.wait()) { - XCTAssertEqualTypeAndValue($0, MyError()) - } - } - - func testCancelFailsTaskAfterRequestIsSent() { - let embeddedEventLoop = EmbeddedEventLoop() - defer { XCTAssertNoThrow(try embeddedEventLoop.syncShutdownGracefully()) } - let logger = Logger(label: "test") - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://swift.org/")) - guard let request = maybeRequest else { return XCTFail("Expected to have a request") } - - let delegate = UploadCountingDelegate(eventLoop: embeddedEventLoop) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embeddedEventLoop), - task: .init(eventLoop: embeddedEventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let bag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag.") } - XCTAssert(bag.eventLoop === embeddedEventLoop) - - let executor = MockRequestExecutor(eventLoop: embeddedEventLoop) - - XCTAssertFalse(executor.isCancelled) - - XCTAssertEqual(delegate.hitDidSendRequestHead, 0) - XCTAssertEqual(delegate.hitDidSendRequest, 0) - executor.runRequest(bag) - XCTAssertEqual(delegate.hitDidSendRequestHead, 1) - XCTAssertEqual(delegate.hitDidSendRequest, 1) - - bag.fail(HTTPClientError.cancelled) - XCTAssertTrue(executor.isCancelled, "The request bag, should call cancel immediately on the executor") - - XCTAssertThrowsError(try bag.task.futureResult.timeout(after: .seconds(10)).wait()) { - XCTAssertEqual($0 as? HTTPClientError, .cancelled) - } - } - - func testCancelFailsTaskWhenTaskIsQueued() { - let embeddedEventLoop = EmbeddedEventLoop() - defer { XCTAssertNoThrow(try embeddedEventLoop.syncShutdownGracefully()) } - let logger = Logger(label: "test") - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://swift.org/")) - guard let request = maybeRequest else { return XCTFail("Expected to have a request") } - - let delegate = UploadCountingDelegate(eventLoop: embeddedEventLoop) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embeddedEventLoop), - task: .init(eventLoop: embeddedEventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let bag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag.") } - - let queuer = MockTaskQueuer() - bag.requestWasQueued(queuer) - - XCTAssertEqual(queuer.hitCancelCount, 0) - bag.fail(HTTPClientError.cancelled) - XCTAssertEqual(queuer.hitCancelCount, 1) - - XCTAssertThrowsError(try bag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .cancelled) - } - } - - func testFailsTaskWhenTaskIsWaitingForMoreFromServer() { - let embeddedEventLoop = EmbeddedEventLoop() - defer { XCTAssertNoThrow(try embeddedEventLoop.syncShutdownGracefully()) } - let logger = Logger(label: "test") - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://swift.org/")) - guard let request = maybeRequest else { return XCTFail("Expected to have a request") } - - let delegate = UploadCountingDelegate(eventLoop: embeddedEventLoop) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embeddedEventLoop), - task: .init(eventLoop: embeddedEventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let bag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag.") } - - let executor = MockRequestExecutor(eventLoop: embeddedEventLoop) - executor.runRequest(bag) - bag.receiveResponseHead(.init(version: .http1_1, status: .ok)) - XCTAssertEqual(executor.isCancelled, false) - bag.fail(HTTPClientError.readTimeout) - XCTAssertEqual(executor.isCancelled, true) - XCTAssertThrowsError(try bag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .readTimeout) - } - } - - func testChannelBecomingWritableDoesntCrashCancelledTask() { - let embeddedEventLoop = EmbeddedEventLoop() - defer { XCTAssertNoThrow(try embeddedEventLoop.syncShutdownGracefully()) } - let logger = Logger(label: "test") - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow( - maybeRequest = try HTTPClient.Request( - url: "https://swift.org/", - body: .bytes([1, 2, 3, 4, 5]) - ) - ) - guard let request = maybeRequest else { return XCTFail("Expected to have a request") } - - let delegate = UploadCountingDelegate(eventLoop: embeddedEventLoop) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embeddedEventLoop), - task: .init(eventLoop: embeddedEventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let bag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag.") } - - let executor = MockRequestExecutor(eventLoop: embeddedEventLoop) - executor.runRequest(bag) - - // This simulates a race between the user cancelling the task (which invokes `RequestBag.fail(_:)`) and the - // call to `resumeRequestBodyStream` (which comes from the `Channel` event loop and so may have to hop. - bag.fail(HTTPClientError.cancelled) - bag.resumeRequestBodyStream() - - XCTAssertEqual(executor.isCancelled, true) - XCTAssertThrowsError(try bag.task.futureResult.wait()) { - XCTAssertEqual($0 as? HTTPClientError, .cancelled) - } - } - - func testDidReceiveBodyPartFailedPromise() { - let embeddedEventLoop = EmbeddedEventLoop() - defer { XCTAssertNoThrow(try embeddedEventLoop.syncShutdownGracefully()) } - let logger = Logger(label: "test") - - var maybeRequest: HTTPClient.Request? - - XCTAssertNoThrow( - maybeRequest = try HTTPClient.Request( - url: "https://swift.org/", - method: .POST, - body: .byteBuffer(.init(bytes: [1])) - ) - ) - guard let request = maybeRequest else { return XCTFail("Expected to have a request") } - - struct MyError: Error, Equatable {} - final class Delegate: HTTPClientResponseDelegate { - typealias Response = Void - let didFinishPromise: EventLoopPromise - init(didFinishPromise: EventLoopPromise) { - self.didFinishPromise = didFinishPromise - } - - func didReceiveBodyPart(task: HTTPClient.Task, _ buffer: ByteBuffer) -> EventLoopFuture { - task.eventLoop.makeFailedFuture(MyError()) - } - - func didReceiveError(task: HTTPClient.Task, _ error: Error) { - self.didFinishPromise.fail(error) - } - - func didFinishRequest(task: AsyncHTTPClient.HTTPClient.Task) throws { - XCTFail("\(#function) should not be called") - self.didFinishPromise.succeed(()) - } - } - let delegate = Delegate(didFinishPromise: embeddedEventLoop.makePromise()) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embeddedEventLoop), - task: .init(eventLoop: embeddedEventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let bag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag.") } - - let executor = MockRequestExecutor(eventLoop: embeddedEventLoop) - - executor.runRequest(bag) - - bag.resumeRequestBodyStream() - XCTAssertNoThrow(try executor.receiveRequestBody { XCTAssertEqual($0, ByteBuffer(bytes: [1])) }) - - bag.receiveResponseHead(.init(version: .http1_1, status: .ok)) - - bag.succeedRequest([ByteBuffer([1])]) - - XCTAssertThrowsError(try delegate.didFinishPromise.futureResult.wait()) { error in - XCTAssertEqualTypeAndValue(error, MyError()) - } - XCTAssertThrowsError(try bag.task.futureResult.wait()) { error in - XCTAssertEqualTypeAndValue(error, MyError()) - } - } - - func testHTTPUploadIsCancelledEvenThoughRequestSucceeds() { - let embeddedEventLoop = EmbeddedEventLoop() - defer { XCTAssertNoThrow(try embeddedEventLoop.syncShutdownGracefully()) } - let logger = Logger(label: "test") - - var maybeRequest: HTTPClient.Request? - let writeSecondPartPromise = embeddedEventLoop.makePromise(of: Void.self) - let firstWriteSuccess: NIOLockedValueBox = .init(false) - - XCTAssertNoThrow( - maybeRequest = try HTTPClient.Request( - url: "https://swift.org/", - method: .POST, - headers: ["content-length": "12"], - body: .stream(contentLength: 12) { writer -> EventLoopFuture in - writer.write(.byteBuffer(.init(bytes: 0...3))).flatMap { _ in - firstWriteSuccess.withLockedValue { $0 = true } - - return writeSecondPartPromise.futureResult - }.flatMap { - writer.write(.byteBuffer(.init(bytes: 4...7))) - }.always { result in - XCTAssertTrue(firstWriteSuccess.withLockedValue { $0 }) - - guard case .failure(let error) = result else { - return XCTFail("Expected the second write to fail") - } - XCTAssertEqual(error as? HTTPClientError, .requestStreamCancelled) - } - } - ) - ) - guard let request = maybeRequest else { return XCTFail("Expected to have a request") } - - let delegate = UploadCountingDelegate(eventLoop: embeddedEventLoop) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embeddedEventLoop), - task: .init(eventLoop: embeddedEventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let bag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag.") } - - let executor = MockRequestExecutor(eventLoop: embeddedEventLoop) - - XCTAssertEqual(delegate.hitDidSendRequestHead, 0) - XCTAssertEqual(delegate.hitDidSendRequest, 0) - executor.runRequest(bag) - XCTAssertEqual(delegate.hitDidSendRequestHead, 1) - XCTAssertEqual(delegate.hitDidSendRequest, 0) - - bag.resumeRequestBodyStream() - XCTAssertNoThrow(try executor.receiveRequestBody { XCTAssertEqual($0, ByteBuffer(bytes: 0...3)) }) - // receive a 301 response immediately. - bag.receiveResponseHead(.init(version: .http1_1, status: .movedPermanently)) - XCTAssertNoThrow(try XCTUnwrap(delegate.backpressurePromise).succeed(())) - bag.succeedRequest(.init()) - - // if we now write our second part of the response this should fail the backpressure promise - writeSecondPartPromise.succeed(()) - - XCTAssertEqual(delegate.receivedHead?.status, .movedPermanently) - XCTAssertNoThrow(try bag.task.futureResult.wait()) - } - - func testRaceBetweenConnectionCloseAndDemandMoreData() { - let embeddedEventLoop = EmbeddedEventLoop() - defer { XCTAssertNoThrow(try embeddedEventLoop.syncShutdownGracefully()) } - let logger = Logger(label: "test") - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://swift.org/")) - guard let request = maybeRequest else { return XCTFail("Expected to have a request") } - - let delegate = UploadCountingDelegate(eventLoop: embeddedEventLoop) - var maybeRequestBag: RequestBag? - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embeddedEventLoop), - task: .init(eventLoop: embeddedEventLoop, logger: logger), - redirectHandler: nil, - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let bag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag.") } - - let executor = MockRequestExecutor(eventLoop: embeddedEventLoop) - executor.runRequest(bag) - bag.receiveResponseHead(.init(version: .http1_1, status: .ok)) - XCTAssertFalse(executor.signalledDemandForResponseBody) - XCTAssertNoThrow(try XCTUnwrap(delegate.backpressurePromise).succeed(())) - XCTAssertTrue(executor.signalledDemandForResponseBody) - executor.resetResponseStreamDemandSignal() - - // "foo" is forwarded for consumption. We expect the RequestBag to consume "foo" with the - // delegate and call demandMoreBody afterwards. - XCTAssertEqual(delegate.hitDidReceiveBodyPart, 0) - bag.receiveResponseBodyParts([ByteBuffer(string: "foo")]) - XCTAssertFalse(executor.signalledDemandForResponseBody) - XCTAssertEqual(delegate.hitDidReceiveBodyPart, 1) - XCTAssertNoThrow(try XCTUnwrap(delegate.backpressurePromise).succeed(())) - XCTAssertTrue(executor.signalledDemandForResponseBody) - executor.resetResponseStreamDemandSignal() - - bag.receiveResponseBodyParts([ByteBuffer(string: "bar")]) - XCTAssertEqual(delegate.hitDidReceiveBodyPart, 2) - - // the remote closes the connection, which leads to more data and a succeed of the request - bag.succeedRequest([ByteBuffer(string: "baz")]) - XCTAssertEqual(delegate.hitDidReceiveBodyPart, 2) - - XCTAssertNoThrow(try XCTUnwrap(delegate.backpressurePromise).succeed(())) - XCTAssertEqual(delegate.hitDidReceiveBodyPart, 3) - - XCTAssertEqual(delegate.hitDidReceiveResponse, 0) - XCTAssertNoThrow(try XCTUnwrap(delegate.backpressurePromise).succeed(())) - XCTAssertEqual(delegate.hitDidReceiveResponse, 1) - } - - func testRedirectWith3KBBody() { - let embeddedEventLoop = EmbeddedEventLoop() - defer { XCTAssertNoThrow(try embeddedEventLoop.syncShutdownGracefully()) } - let logger = Logger(label: "test") - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://swift.org/")) - guard let request = maybeRequest else { return XCTFail("Expected to have a request") } - - let delegate = UploadCountingDelegate(eventLoop: embeddedEventLoop) - var maybeRequestBag: RequestBag? - var redirectTriggered = false - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embeddedEventLoop), - task: .init(eventLoop: embeddedEventLoop, logger: logger), - redirectHandler: .init( - request: request, - redirectState: RedirectState( - .follow(max: 5, allowCycles: false), - initialURL: request.url.absoluteString - )!, - execute: { request, _ in - XCTAssertEqual(request.url.absoluteString, "https://swift.org/sswg") - XCTAssertFalse(redirectTriggered) - - let task = HTTPClient.Task( - eventLoop: embeddedEventLoop, - logger: logger - ) - task.promise.fail(HTTPClientError.cancelled) - redirectTriggered = true - return task - } - ), - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let bag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag.") } - - let executor = MockRequestExecutor(eventLoop: embeddedEventLoop) - executor.runRequest(bag) - XCTAssertFalse(executor.signalledDemandForResponseBody) - XCTAssertTrue(delegate.history.isEmpty) - let responseHead = HTTPResponseHead( - version: .http1_1, - status: .permanentRedirect, - headers: ["content-length": "\(3 * 1024)", "location": "https://swift.org/sswg"] - ) - bag.receiveResponseHead(responseHead) - XCTAssertEqual(delegate.history.map(\.request.url), [request.url]) - XCTAssertEqual(delegate.history.map(\.response), [responseHead]) - XCTAssertNil(delegate.backpressurePromise) - XCTAssertTrue(executor.signalledDemandForResponseBody) - executor.resetResponseStreamDemandSignal() - - // "foo" is forwarded for consumption. We expect the RequestBag to consume "foo" with the - // delegate and call demandMoreBody afterwards. - XCTAssertEqual(delegate.hitDidReceiveBodyPart, 0) - XCTAssertFalse(executor.signalledDemandForResponseBody) - bag.receiveResponseBodyParts([ByteBuffer(repeating: 0, count: 1024)]) - XCTAssertTrue(executor.signalledDemandForResponseBody) - XCTAssertEqual(delegate.hitDidReceiveBodyPart, 0) - XCTAssertNil(delegate.backpressurePromise) - executor.resetResponseStreamDemandSignal() - - XCTAssertEqual(delegate.hitDidReceiveBodyPart, 0) - XCTAssertFalse(executor.signalledDemandForResponseBody) - bag.receiveResponseBodyParts([ByteBuffer(repeating: 1, count: 1024)]) - XCTAssertTrue(executor.signalledDemandForResponseBody) - XCTAssertEqual(delegate.hitDidReceiveBodyPart, 0) - XCTAssertNil(delegate.backpressurePromise) - executor.resetResponseStreamDemandSignal() - - XCTAssertEqual(delegate.hitDidReceiveBodyPart, 0) - XCTAssertFalse(executor.signalledDemandForResponseBody) - bag.succeedRequest([ByteBuffer(repeating: 2, count: 1024)]) - XCTAssertFalse(executor.signalledDemandForResponseBody) - XCTAssertEqual(delegate.hitDidReceiveResponse, 0) - XCTAssertNil(delegate.backpressurePromise) - executor.resetResponseStreamDemandSignal() - - XCTAssertTrue(redirectTriggered) - } - - func testRedirectWith4KBBodyAnnouncedInResponseHead() { - let embeddedEventLoop = EmbeddedEventLoop() - defer { XCTAssertNoThrow(try embeddedEventLoop.syncShutdownGracefully()) } - let logger = Logger(label: "test") - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://swift.org/")) - guard let request = maybeRequest else { return XCTFail("Expected to have a request") } - - let delegate = UploadCountingDelegate(eventLoop: embeddedEventLoop) - var maybeRequestBag: RequestBag? - var redirectTriggered = false - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embeddedEventLoop), - task: .init(eventLoop: embeddedEventLoop, logger: logger), - redirectHandler: .init( - request: request, - redirectState: RedirectState( - .follow(max: 5, allowCycles: false), - initialURL: request.url.absoluteString - )!, - execute: { request, _ in - XCTAssertEqual(request.url.absoluteString, "https://swift.org/sswg") - XCTAssertFalse(redirectTriggered) - - let task = HTTPClient.Task( - eventLoop: embeddedEventLoop, - logger: logger - ) - task.promise.fail(HTTPClientError.cancelled) - redirectTriggered = true - return task - } - ), - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let bag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag.") } - - let executor = MockRequestExecutor(eventLoop: embeddedEventLoop) - executor.runRequest(bag) - XCTAssertFalse(executor.signalledDemandForResponseBody) - XCTAssertTrue(delegate.history.isEmpty) - let responseHead = HTTPResponseHead( - version: .http1_1, - status: .permanentRedirect, - headers: ["content-length": "\(4 * 1024)", "location": "https://swift.org/sswg"] - ) - bag.receiveResponseHead(responseHead) - XCTAssertEqual(delegate.history.map(\.request.url), [request.url]) - XCTAssertEqual(delegate.history.map(\.response), [responseHead]) - XCTAssertNil(delegate.backpressurePromise) - XCTAssertFalse(executor.signalledDemandForResponseBody) - XCTAssertTrue(executor.isCancelled) - - XCTAssertTrue(redirectTriggered) - } - - func testRedirectWith4KBBodyNotAnnouncedInResponseHead() { - let embeddedEventLoop = EmbeddedEventLoop() - defer { XCTAssertNoThrow(try embeddedEventLoop.syncShutdownGracefully()) } - let logger = Logger(label: "test") - - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow(maybeRequest = try HTTPClient.Request(url: "https://swift.org/")) - guard let request = maybeRequest else { return XCTFail("Expected to have a request") } - - let delegate = UploadCountingDelegate(eventLoop: embeddedEventLoop) - var maybeRequestBag: RequestBag? - var redirectTriggered = false - XCTAssertNoThrow( - maybeRequestBag = try RequestBag( - request: request, - eventLoopPreference: .delegate(on: embeddedEventLoop), - task: .init(eventLoop: embeddedEventLoop, logger: logger), - redirectHandler: .init( - request: request, - redirectState: RedirectState( - .follow(max: 5, allowCycles: false), - initialURL: request.url.absoluteString - )!, - execute: { request, _ in - XCTAssertEqual(request.url.absoluteString, "https://swift.org/sswg") - XCTAssertFalse(redirectTriggered) - - let task = HTTPClient.Task( - eventLoop: embeddedEventLoop, - logger: logger - ) - task.promise.fail(HTTPClientError.cancelled) - redirectTriggered = true - return task - } - ), - connectionDeadline: .now() + .seconds(30), - requestOptions: .forTests(), - delegate: delegate - ) - ) - guard let bag = maybeRequestBag else { return XCTFail("Expected to be able to create a request bag.") } - - let executor = MockRequestExecutor(eventLoop: embeddedEventLoop) - executor.runRequest(bag) - XCTAssertFalse(executor.signalledDemandForResponseBody) - XCTAssertTrue(delegate.history.isEmpty) - let responseHead = HTTPResponseHead( - version: .http1_1, - status: .permanentRedirect, - headers: ["content-length": "\(3 * 1024)", "location": "https://swift.org/sswg"] - ) - bag.receiveResponseHead(responseHead) - XCTAssertEqual(delegate.history.map(\.request.url), [request.url]) - XCTAssertEqual(delegate.history.map(\.response), [responseHead]) - XCTAssertNil(delegate.backpressurePromise) - XCTAssertTrue(executor.signalledDemandForResponseBody) - executor.resetResponseStreamDemandSignal() - - // "foo" is forwarded for consumption. We expect the RequestBag to consume "foo" with the - // delegate and call demandMoreBody afterwards. - XCTAssertEqual(delegate.hitDidReceiveBodyPart, 0) - XCTAssertFalse(executor.signalledDemandForResponseBody) - bag.receiveResponseBodyParts([ByteBuffer(repeating: 0, count: 2024)]) - XCTAssertTrue(executor.signalledDemandForResponseBody) - XCTAssertEqual(delegate.hitDidReceiveBodyPart, 0) - XCTAssertNil(delegate.backpressurePromise) - executor.resetResponseStreamDemandSignal() - - XCTAssertEqual(delegate.hitDidReceiveBodyPart, 0) - XCTAssertFalse(executor.isCancelled) - XCTAssertFalse(executor.signalledDemandForResponseBody) - bag.receiveResponseBodyParts([ByteBuffer(repeating: 1, count: 2024)]) - XCTAssertFalse(executor.signalledDemandForResponseBody) - XCTAssertTrue(executor.isCancelled) - XCTAssertEqual(delegate.hitDidReceiveBodyPart, 0) - XCTAssertNil(delegate.backpressurePromise) - executor.resetResponseStreamDemandSignal() - - XCTAssertTrue(redirectTriggered) - } - - func testWeDontLeakTheRequestIfTheRequestWriterWasCapturedByAPromise() { - final class LeakDetector: Sendable {} - - let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) - defer { XCTAssertNoThrow(try group.syncShutdownGracefully()) } - - let httpClient = HTTPClient(eventLoopGroupProvider: .shared(group)) - defer { XCTAssertNoThrow(try httpClient.shutdown().wait()) } - - let httpBin = HTTPBin() - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - var leakDetector = LeakDetector() - - do { - var maybeRequest: HTTPClient.Request? - XCTAssertNoThrow( - maybeRequest = try HTTPClient.Request(url: "http://localhost/(httpBin.port)/", method: .POST) - ) - guard var request = maybeRequest else { return XCTFail("Expected to have a request here") } - - let writerPromise = group.any().makePromise(of: HTTPClient.Body.StreamWriter.self) - let donePromise = group.any().makePromise(of: Void.self) - request.body = .stream { [leakDetector] writer in - _ = leakDetector - writerPromise.succeed(writer) - return donePromise.futureResult - } - - let resultFuture = httpClient.execute(request: request) - request.body = nil - writerPromise.futureResult.whenSuccess { writer in - writer.write(.byteBuffer(ByteBuffer(string: "hello"))).map { - print("written") - }.cascade(to: donePromise) - } - XCTAssertNoThrow(try donePromise.futureResult.wait()) - print("HTTP sent") - - var result: HTTPClient.Response? - XCTAssertNoThrow(result = try resultFuture.wait()) - - XCTAssertEqual(.ok, result?.status) - let body = result?.body.map { String(buffer: $0) } - XCTAssertNotNil(body) - print("HTTP done") - } - XCTAssertTrue(isKnownUniquelyReferenced(&leakDetector)) - } -} - -extension HTTPClient.Task { - convenience init( - eventLoop: EventLoop, - logger: Logger - ) { - self.init(eventLoop: eventLoop, logger: logger) { - preconditionFailure("thread pool not needed in tests") - } - } -} - -final class UploadCountingDelegate: HTTPClientResponseDelegate { - typealias Response = Void - - let eventLoop: EventLoop - - struct State: Sendable { - var hitDidSendRequestHead = 0 - var hitDidSendRequestPart = 0 - var hitDidSendRequest = 0 - var hitDidReceiveResponse = 0 - var hitDidReceiveBodyPart = 0 - var hitDidReceiveError = 0 - - var history: [(request: HTTPClient.Request, response: HTTPResponseHead)] = [] - var receivedHead: HTTPResponseHead? - var lastBodyPart: ByteBuffer? - var backpressurePromise: EventLoopPromise? - var lastError: Error? - } - - private let state: NIOLoopBoundBox - - var hitDidSendRequestHead: Int { self.state.value.hitDidSendRequestHead } - var hitDidSendRequestPart: Int { self.state.value.hitDidSendRequestPart } - var hitDidSendRequest: Int { self.state.value.hitDidSendRequest } - var hitDidReceiveResponse: Int { self.state.value.hitDidReceiveResponse } - var hitDidReceiveBodyPart: Int { self.state.value.hitDidReceiveBodyPart } - var hitDidReceiveError: Int { self.state.value.hitDidReceiveError } - - var history: [(request: HTTPClient.Request, response: HTTPResponseHead)] { - self.state.value.history - } - var receivedHead: HTTPResponseHead? { self.state.value.receivedHead } - var lastBodyPart: ByteBuffer? { self.state.value.lastBodyPart } - var backpressurePromise: EventLoopPromise? { self.state.value.backpressurePromise } - var lastError: Error? { self.state.value.lastError } - - init(eventLoop: EventLoop) { - self.eventLoop = eventLoop - self.state = .makeBoxSendingValue(State(), eventLoop: eventLoop) - } - - func didSendRequestHead(task: HTTPClient.Task, _ head: HTTPRequestHead) { - self.state.value.hitDidSendRequestHead += 1 - } - - func didSendRequestPart(task: HTTPClient.Task, _ part: IOData) { - self.state.value.hitDidSendRequestPart += 1 - } - - func didSendRequest(task: HTTPClient.Task) { - self.state.value.hitDidSendRequest += 1 - } - - func didVisitURL(task: HTTPClient.Task, _ request: HTTPClient.Request, _ head: HTTPResponseHead) { - self.state.value.history.append((request, head)) - } - - func didReceiveHead(task: HTTPClient.Task, _ head: HTTPResponseHead) -> EventLoopFuture { - self.state.value.receivedHead = head - return self.createBackpressurePromise() - } - - func didReceiveBodyPart(task: HTTPClient.Task, _ buffer: ByteBuffer) -> EventLoopFuture { - assert(self.state.value.backpressurePromise == nil) - self.state.value.hitDidReceiveBodyPart += 1 - self.state.value.lastBodyPart = buffer - return self.createBackpressurePromise() - } - - func didFinishRequest(task: HTTPClient.Task) throws { - self.state.value.hitDidReceiveResponse += 1 - } - - func didReceiveError(task: HTTPClient.Task, _ error: Error) { - self.state.value.hitDidReceiveError += 1 - self.state.value.lastError = error - } - - private func createBackpressurePromise() -> EventLoopFuture { - assert(self.state.value.backpressurePromise == nil) - self.state.value.backpressurePromise = self.eventLoop.makePromise(of: Void.self) - return self.state.value.backpressurePromise!.futureResult.always { _ in - self.state.value.backpressurePromise = nil - } - } -} - -final class MockTaskQueuer: HTTPRequestScheduler { - private let _hitCancelCount = ManagedAtomic(0) - - var hitCancelCount: Int { - self._hitCancelCount.load(ordering: .sequentiallyConsistent) - } - - let onCancelRequest: (@Sendable (HTTPSchedulableRequest) -> Void)? - - init(onCancelRequest: (@Sendable (HTTPSchedulableRequest) -> Void)? = nil) { - self.onCancelRequest = onCancelRequest - } - - func cancelRequest(_ request: HTTPSchedulableRequest) { - self._hitCancelCount.wrappingIncrement(ordering: .sequentiallyConsistent) - self.onCancelRequest?(request) - } -} - -extension RequestOptions { - static func forTests( - idleReadTimeout: TimeAmount? = nil, - idleWriteTimeout: TimeAmount? = nil, - dnsOverride: [String: String] = [:] - ) -> Self { - RequestOptions( - idleReadTimeout: idleReadTimeout, - idleWriteTimeout: idleWriteTimeout, - dnsOverride: dnsOverride - ) - } -} diff --git a/Tests/AsyncHTTPClientTests/RequestValidationTests.swift b/Tests/AsyncHTTPClientTests/RequestValidationTests.swift deleted file mode 100644 index ea5a6bd66..000000000 --- a/Tests/AsyncHTTPClientTests/RequestValidationTests.swift +++ /dev/null @@ -1,392 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2020 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOHTTP1 -import XCTest - -@testable import AsyncHTTPClient - -class RequestValidationTests: XCTestCase { - func testContentLengthHeaderIsRemovedFromGETIfNoBody() { - var headers = HTTPHeaders([("Content-Length", "0")]) - var metadata: RequestFramingMetadata? - XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: .GET, bodyLength: .known(0))) - XCTAssertNil(headers.first(name: "Content-Length")) - XCTAssertEqual(metadata?.body, .fixedSize(0)) - } - - func testContentLengthHeaderIsAddedToPOSTAndPUTWithNoBody() { - var putHeaders = HTTPHeaders() - var putMetadata: RequestFramingMetadata? - XCTAssertNoThrow( - putMetadata = try putHeaders.validateAndSetTransportFraming(method: .PUT, bodyLength: .known(0)) - ) - XCTAssertEqual(putHeaders.first(name: "Content-Length"), "0") - XCTAssertEqual(putMetadata?.body, .fixedSize(0)) - - var postHeaders = HTTPHeaders() - var postMetadata: RequestFramingMetadata? - XCTAssertNoThrow( - postMetadata = try postHeaders.validateAndSetTransportFraming(method: .POST, bodyLength: .known(0)) - ) - XCTAssertEqual(postHeaders.first(name: "Content-Length"), "0") - XCTAssertEqual(postMetadata?.body, .fixedSize(0)) - } - - func testContentLengthHeaderIsChangedIfBodyHasDifferentLength() { - var headers = HTTPHeaders([("Content-Length", "0")]) - var metadata: RequestFramingMetadata? - XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: .PUT, bodyLength: .known(200))) - XCTAssertEqual(headers.first(name: "Content-Length"), "200") - XCTAssertEqual(metadata?.body, .fixedSize(200)) - } - - func testTRACERequestMustNotHaveBody() { - for header in [("Content-Length", "200"), ("Transfer-Encoding", "chunked")] { - var headers = HTTPHeaders([header]) - XCTAssertThrowsError(try headers.validateAndSetTransportFraming(method: .TRACE, bodyLength: .known(200))) { - XCTAssertEqual($0 as? HTTPClientError, .traceRequestWithBody) - } - } - } - - func testGET_HEAD_DELETE_CONNECTRequestCanHaveBody() { - // GET, HEAD, DELETE and CONNECT requests can have a payload. (though uncommon) - let allowedMethods: [HTTPMethod] = [.GET, .HEAD, .DELETE, .CONNECT] - var headers = HTTPHeaders() - for method in allowedMethods { - XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(100))) - } - } - - func testInvalidHeaderFieldNames() { - var headers = HTTPHeaders([ - ("Content-Length", "200"), - ("User Agent", "Haha"), - ]) - - XCTAssertThrowsError(try headers.validateAndSetTransportFraming(method: .GET, bodyLength: .known(0))) { error in - XCTAssertEqual(error as? HTTPClientError, HTTPClientError.invalidHeaderFieldNames(["User Agent"])) - } - } - - func testValidHeaderFieldNames() { - var headers = HTTPHeaders([ - ("abcdefghijklmnopqrstuvwxyz", "Haha"), - ("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "Haha"), - ("0123456789", "Haha"), - ("!#$%&'*+-.^_`|~", "Haha"), - ]) - - XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: .GET, bodyLength: .known(0))) - } - - func testMetadataDetectConnectionClose() { - var headers = HTTPHeaders([ - ("Connection", "close") - ]) - var metadata: RequestFramingMetadata? - XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: .GET, bodyLength: .known(0))) - XCTAssertEqual(metadata?.connectionClose, true) - } - - func testMetadataDefaultIsConnectionCloseIsFalse() { - var headers = HTTPHeaders([]) - var metadata: RequestFramingMetadata? - XCTAssertNoThrow(metadata = try headers.validateAndSetTransportFraming(method: .GET, bodyLength: .known(0))) - XCTAssertEqual(metadata?.connectionClose, false) - } - - // MARK: - Content-Length/Transfer-Encoding Matrix - - // Method kind User sets Body Expectation - // ---------------------------------------------------------------------------------- - // .GET, .HEAD, .DELETE, .CONNECT, .TRACE nothing nil Neither CL nor chunked - // other nothing nil CL=0 - func testNoHeadersNoBody() throws { - for method: HTTPMethod in [.GET, .HEAD, .DELETE, .CONNECT, .TRACE] { - var headers: HTTPHeaders = .init() - var metadata: RequestFramingMetadata? - XCTAssertNoThrow( - metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(0)) - ) - XCTAssertTrue(headers["content-length"].isEmpty) - XCTAssertTrue(headers["transfer-encoding"].isEmpty) - XCTAssertEqual(metadata?.body, .fixedSize(0)) - } - - for method: HTTPMethod in [.POST, .PUT] { - var headers: HTTPHeaders = .init() - var metadata: RequestFramingMetadata? - XCTAssertNoThrow( - metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(0)) - ) - XCTAssertEqual(headers["content-length"].first, "0") - XCTAssertFalse(headers["transfer-encoding"].contains("chunked")) - XCTAssertEqual(metadata?.body, .fixedSize(0)) - } - } - - // Method kind User sets Body Expectation - // -------------------------------------------------------------------------------------- - // .GET, .HEAD, .DELETE, .CONNECT, .TRACE nothing not nil CL or chunked - // other nothing not nil CL or chunked - func testNoHeadersHasBody() throws { - // Body length is known - for method: HTTPMethod in [.GET, .HEAD, .DELETE, .CONNECT] { - var headers: HTTPHeaders = .init() - var metadata: RequestFramingMetadata? - XCTAssertNoThrow( - metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(1)) - ) - XCTAssertEqual(headers["content-length"].first, "1") - XCTAssertTrue(headers["transfer-encoding"].isEmpty) - XCTAssertEqual(metadata?.body, .fixedSize(1)) - } - - // Body length is _not_ known - for method: HTTPMethod in [.GET, .HEAD, .DELETE, .CONNECT] { - var headers: HTTPHeaders = .init() - var metadata: RequestFramingMetadata? - XCTAssertNoThrow( - metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .unknown) - ) - XCTAssertTrue(headers["content-length"].isEmpty) - XCTAssertTrue(headers["transfer-encoding"].contains("chunked")) - XCTAssertEqual(metadata?.body, .stream) - } - - // Body length is known - for method: HTTPMethod in [.POST, .PUT] { - var headers: HTTPHeaders = .init() - var metadata: RequestFramingMetadata? - XCTAssertNoThrow( - metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(1)) - ) - XCTAssertEqual(headers["content-length"].first, "1") - XCTAssertTrue(headers["transfer-encoding"].isEmpty) - XCTAssertEqual(metadata?.body, .fixedSize(1)) - } - - // Body length is _not_ known - for method: HTTPMethod in [.POST, .PUT] { - var headers: HTTPHeaders = .init() - var metadata: RequestFramingMetadata? - XCTAssertNoThrow( - metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .unknown) - ) - XCTAssertTrue(headers["content-length"].isEmpty) - XCTAssertTrue(headers["transfer-encoding"].contains("chunked")) - XCTAssertEqual(metadata?.body, .stream) - } - } - - // Method kind User sets Body Expectation - // ------------------------------------------------------------------------------ - // .GET, .HEAD, .DELETE, .CONNECT, .TRACE content-length nil Neither CL nor chunked - // other content-length nil CL=0 - func testContentLengthHeaderNoBody() throws { - for method: HTTPMethod in [.GET, .HEAD, .DELETE, .CONNECT, .TRACE] { - var headers: HTTPHeaders = .init([("Content-Length", "1")]) - var metadata: RequestFramingMetadata? - XCTAssertNoThrow( - metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(0)) - ) - XCTAssertTrue(headers["content-length"].isEmpty) - XCTAssertTrue(headers["transfer-encoding"].isEmpty) - XCTAssertEqual(metadata?.body, .fixedSize(0)) - } - - for method: HTTPMethod in [.POST, .PUT] { - var headers: HTTPHeaders = .init([("Content-Length", "1")]) - var metadata: RequestFramingMetadata? - XCTAssertNoThrow( - metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(0)) - ) - XCTAssertEqual(headers["content-length"].first, "0") - XCTAssertTrue(headers["transfer-encoding"].isEmpty) - XCTAssertEqual(metadata?.body, .fixedSize(0)) - } - } - - // Method kind User sets Body Expectation - // -------------------------------------------------------------------------- - // .GET, .HEAD, .DELETE, .CONNECT content-length not nil CL=1 - // other content-length nit nil CL=1 - func testContentLengthHeaderHasBody() throws { - for method: HTTPMethod in [.GET, .HEAD, .DELETE, .CONNECT] { - var headers: HTTPHeaders = .init([("Content-Length", "1")]) - var metadata: RequestFramingMetadata? - XCTAssertNoThrow( - metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(1)) - ) - XCTAssertEqual(headers["content-length"].first, "1") - XCTAssertTrue(headers["transfer-encoding"].isEmpty) - XCTAssertEqual(metadata?.body, .fixedSize(1)) - } - - for method: HTTPMethod in [.POST, .PUT] { - var headers: HTTPHeaders = .init([("Content-Length", "1")]) - var metadata: RequestFramingMetadata? - XCTAssertNoThrow( - metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(1)) - ) - XCTAssertEqual(headers["content-length"].first, "1") - XCTAssertTrue(headers["transfer-encoding"].isEmpty) - XCTAssertEqual(metadata?.body, .fixedSize(1)) - } - } - - // Method kind User sets Body Expectation - // ------------------------------------------------------------------------------------------ - // .GET, .HEAD, .DELETE, .CONNECT, .TRACE transfer-encoding: chunked nil nil - // other transfer-encoding: chunked nil nil - func testTransferEncodingHeaderNoBody() throws { - for method: HTTPMethod in [.GET, .HEAD, .DELETE, .CONNECT, .TRACE] { - var headers: HTTPHeaders = .init([("Transfer-Encoding", "chunked")]) - var metadata: RequestFramingMetadata? - XCTAssertNoThrow( - metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(0)) - ) - XCTAssertTrue(headers["content-length"].isEmpty) - XCTAssertFalse(headers["transfer-encoding"].contains("chunked")) - XCTAssertEqual(metadata?.body, .fixedSize(0)) - } - - for method: HTTPMethod in [.POST, .PUT] { - var headers: HTTPHeaders = .init([("Transfer-Encoding", "chunked")]) - var metadata: RequestFramingMetadata? - XCTAssertNoThrow( - metadata = try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(0)) - ) - XCTAssertEqual(headers["content-length"].first, "0") - XCTAssertFalse(headers["transfer-encoding"].contains("chunked")) - XCTAssertEqual(metadata?.body, .fixedSize(0)) - } - } - - // Method kind User sets Body Expectation - // -------------------------------------------------------------------------------------- - // .GET, .HEAD, .DELETE, .CONNECT transfer-encoding: chunked not nil no header - // other transfer-encoding: chunked not nil content-length - func testTransferEncodingHeaderHasBody() throws { - for method: HTTPMethod in [.GET, .HEAD, .DELETE, .CONNECT] { - var headers: HTTPHeaders = .init([("Transfer-Encoding", "chunked")]) - XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(1))) - XCTAssertEqual(headers, ["Content-Length": "1"]) - } - - for method: HTTPMethod in [.POST, .PUT] { - var headers: HTTPHeaders = .init([("Transfer-Encoding", "chunked")]) - XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(1))) - XCTAssertEqual(headers, ["Content-Length": "1"]) - } - } - - // Method kind User sets Body Expectation - // --------------------------------------------------------------------------------------- - // .GET, .HEAD, .DELETE, .CONNECT, .TRACE CL & chunked (illegal) nil no header - // other CL & chunked (illegal) nil content-length - func testBothHeadersNoBody() throws { - for method: HTTPMethod in [.GET, .HEAD, .DELETE, .CONNECT, .TRACE] { - var headers: HTTPHeaders = .init([("Content-Length", "1"), ("Transfer-Encoding", "chunked")]) - XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(0))) - XCTAssertEqual(headers, [:]) - } - - for method: HTTPMethod in [.POST, .PUT] { - var headers: HTTPHeaders = .init([("Content-Length", "1"), ("Transfer-Encoding", "chunked")]) - XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(0))) - XCTAssertEqual(headers, ["Content-Length": "0"]) - } - } - - // Method kind User sets Body Expectation - // ------------------------------------------------------------------------------------------- - // .TRACE CL & chunked (illegal) not nil throws error - // other CL & chunked (illegal) not nil content-length - func testBothHeadersHasBody() throws { - for method: HTTPMethod in [.TRACE] { - var headers: HTTPHeaders = .init([("Content-Length", "1"), ("Transfer-Encoding", "chunked")]) - XCTAssertThrowsError(try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(1))) - } - - for method: HTTPMethod in [.GET, .HEAD, .DELETE, .CONNECT, .POST, .PUT] { - var headers: HTTPHeaders = .init([("Content-Length", "1"), ("Transfer-Encoding", "chunked")]) - XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: method, bodyLength: .known(1))) - XCTAssertEqual(headers, ["Content-Length": "1"]) - } - } - - func testHostHeaderIsSetCorrectlyInCreateRequestHead() { - let req1 = try! HTTPClient.Request(url: "http://localhost/get") - XCTAssertEqual(try req1.createRequestHead().0.headers["host"].first, "localhost") - - let req2 = try! HTTPClient.Request(url: "https://localhost/get") - XCTAssertEqual(try req2.createRequestHead().0.headers["host"].first, "localhost") - - let req3 = try! HTTPClient.Request(url: "http://localhost:8080/get") - XCTAssertEqual(try req3.createRequestHead().0.headers["host"].first, "localhost:8080") - - let req4 = try! HTTPClient.Request(url: "http://localhost:443/get") - XCTAssertEqual(try req4.createRequestHead().0.headers["host"].first, "localhost:443") - - let req5 = try! HTTPClient.Request(url: "https://localhost:80/get") - XCTAssertEqual(try req5.createRequestHead().0.headers["host"].first, "localhost:80") - - let req6 = try! HTTPClient.Request(url: "https://localhost/get", headers: ["host": "foo"]) - XCTAssertEqual(try req6.createRequestHead().0.headers["host"].first, "foo") - } - - func testTraceMethodIsNotAllowedToHaveAFixedLengthBody() { - var headers = HTTPHeaders() - XCTAssertThrowsError(try headers.validateAndSetTransportFraming(method: .TRACE, bodyLength: .known(10))) { - XCTAssertEqual($0 as? HTTPClientError, .traceRequestWithBody) - } - } - - func testTraceMethodIsNotAllowedToHaveADynamicLengthBody() { - var headers = HTTPHeaders() - XCTAssertThrowsError(try headers.validateAndSetTransportFraming(method: .TRACE, bodyLength: .unknown)) { - XCTAssertEqual($0 as? HTTPClientError, .traceRequestWithBody) - } - } - - func testTransferEncodingsAreOverwrittenIfBodyLengthIsFixed() { - var headers: HTTPHeaders = [ - "Transfer-Encoding": "gzip, chunked" - ] - XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: .POST, bodyLength: .known(1))) - XCTAssertEqual( - headers, - [ - "Content-Length": "1" - ] - ) - } - - func testTransferEncodingsAreOverwrittenIfBodyLengthIsDynamic() { - var headers: HTTPHeaders = [ - "Transfer-Encoding": "gzip, chunked" - ] - XCTAssertNoThrow(try headers.validateAndSetTransportFraming(method: .POST, bodyLength: .unknown)) - XCTAssertEqual( - headers, - [ - "Transfer-Encoding": "chunked" - ] - ) - } -} diff --git a/Tests/AsyncHTTPClientTests/Resources/example.com.cert.pem b/Tests/AsyncHTTPClientTests/Resources/example.com.cert.pem deleted file mode 100644 index f16590cde..000000000 --- a/Tests/AsyncHTTPClientTests/Resources/example.com.cert.pem +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBwTCCAUigAwIBAgIUX7f9BABxGdAqG5EvLpQScFt9lOkwCgYIKoZIzj0EAwMw -KjEUMBIGA1UECgwLU2VsZiBTaWduZWQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0y -NTA0MDExNDMwMTFaFw0yNjA0MDExNDMwMTFaMCoxFDASBgNVBAoMC1NlbGYgU2ln -bmVkMRIwEAYDVQQDDAlsb2NhbGhvc3QwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQW -szfO5HCWIWgKUqyXUU0pFpYgaq01RRL69XZz1CkV6XTrxMfIvvwez2886EQDL8QX -i5NpKg3qvPgWuDjVHaj4WEJe5XMNqcujxcTufBlmaQ6o4vtoK7CIHDIDldF/HRij -LzAtMBYGA1UdEQQPMA2CC2V4YW1wbGUuY29tMBMGA1UdJQQMMAoGCCsGAQUFBwMB -MAoGCCqGSM49BAMDA2cAMGQCMBJ8Dxg0qX2bEZ3r6dI3UCGAUYxJDVk+XhiIY1Fm -5FJeQqhaVayCRPrPXXGZUJGY/wIwXej70FwkxHKLq+XxfHTC5CzmoOK469C9Rk9Y -ucddXM83ebFxVNgRCWetH9tDdXJ9 ------END CERTIFICATE----- \ No newline at end of file diff --git a/Tests/AsyncHTTPClientTests/Resources/example.com.private-key.pem b/Tests/AsyncHTTPClientTests/Resources/example.com.private-key.pem deleted file mode 100644 index 3ad9ce79e..000000000 --- a/Tests/AsyncHTTPClientTests/Resources/example.com.private-key.pem +++ /dev/null @@ -1,6 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDD9v51MTOcgFIbiHbok -U+QOubosGF1u1q+D3fEUb1U2cgjCofKmPHekXTz0xu9MJi2hZANiAAQWszfO5HCW -IWgKUqyXUU0pFpYgaq01RRL69XZz1CkV6XTrxMfIvvwez2886EQDL8QXi5NpKg3q -vPgWuDjVHaj4WEJe5XMNqcujxcTufBlmaQ6o4vtoK7CIHDIDldF/HRg= ------END PRIVATE KEY----- \ No newline at end of file diff --git a/Tests/AsyncHTTPClientTests/Resources/self_signed_cert.pem b/Tests/AsyncHTTPClientTests/Resources/self_signed_cert.pem deleted file mode 100644 index 20b46f355..000000000 --- a/Tests/AsyncHTTPClientTests/Resources/self_signed_cert.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEpjCCAo4CCQCeTmfiTQcJrzANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls -b2NhbGhvc3QwIBcNMjIwNjE0MTI1NDQ4WhgPMjI5NjAzMjgxMjU0NDhaMBQxEjAQ -BgNVBAMMCWxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB -AK16gPDwP/Xbaf36x5BNd6yHDxCPIIJP4JLfMEuozwLE0YRqwmZOuklb4jUbAXf7 -u9u24ANrC4XS6VVWkfPdugokAUkaKPpwkV4GOiMCXeSDjDiLt1dYxlbp+MLV78a5 -oUDbCAqfFKebIgv1oiK+L6/p818eAHSBWEXXMhTeBDEQAIpJLTG88iVu6r3fMJeH -FbMWuPmAajmx2AEGmwD1x6+NHZLJv1zaufa7j0sHADraagXnfKn6rkLn1is6QFu4 -v7xaNlEwsRCYbh0nrtCtEdJIqnEHc0GCu/gnw5GE3CuRG3FYBTZStIF7d9h+XZQB -ky/YEWSGw9DXFBbebOZugopvl91qaZLqo6Wg0J8qCodgFtJHOSVMq/SAOBmKyw+b -7FYZbj4tQKpuuhwCN+gwEveTy+BK+zGY/sVzPwR8PNjpCgT/HiOBM7dNt4+2r9pY -Ld/mcMvakgRzM4Iqqntem9ltuckZev0TRjdrIylVWsAlNYVXm4ncMLkbzxFkv5Gb -AlhAuTwxyFkIo0M7+GS4lXCZ2bX2umJ0DTl3/NGJserFdkOhvHZSHHC9BzDBysmc -SejX/cGOFQ8O3sFeJdVMGlO64dU482O0FbBcLHmTLXWR4t8dlhrzJuXZ4X6WtHqY -83RwyD1gacYRZnT0eL+Z7XGrO1/qypji1RNaFIaGUt7DAgMBAAEwDQYJKoZIhvcN -AQELBQADggIBAIigOuEVirgqXoUMStTwYObs/DcNIPEugn9gAq9Lt1cr6fm7CvhG -AupxoJTbKLHQX6FegvFSA+4Kt3KYXX9Qi9SJF3Vr4zOhV0q203d4Aui6Lamo5Yye -nhbzzXuDSIyxpaWPFRC2RqCA6+hV8/Ar9Bx0TCI4NQxWxQEPerwqzqWCuTbViccw -WzlwRD2AHibaQaCbpzXg9lOX0fRJHoSM3exYQd91pDoSoL3f/EV3I/czssq+10M8 -F4GhE4bQjaKD7jL5U59dlvfy73nLAzzxzsxsFuYTAgzZwDg586sdbrqqFjzjoZ9A -dF8NuVYkHyFDQkpe66e1isNZi7eFdSjeVmj8llp4b6in59ik7ZS7arzGOxhZZzmv -Jf3nfE4hJzMS/4GJsKMdtcI+6K+hMi6Yt9OoPh82SQ2q8gK4QSWWrwAKuQ4F4UeO -pgiWBryKrkOXlGARBbsR/ZDhlqyAskeGuhIpEY5NLCByFfQ5KlcrX+n4TVLRZMvb -/7PZqboGgU+CUVawm/suPAs8jOlFQOzrxWQPRfWVvFII62ABgozS8N/xZ/WbgTVj -kOtWj85NpaBSCUliIY/7z1FkjpMZO8Kds45WQzAq4YChDLZGbgV0MkyXqO/LEYFJ -zqGOP1yGxVcKxu6t8Xh0hL6JPFmKWiMEWVrd1wut6NAIu6WNftmWZX6J ------END CERTIFICATE----- diff --git a/Tests/AsyncHTTPClientTests/Resources/self_signed_key.pem b/Tests/AsyncHTTPClientTests/Resources/self_signed_key.pem deleted file mode 100644 index 8811c2d81..000000000 --- a/Tests/AsyncHTTPClientTests/Resources/self_signed_key.pem +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQCteoDw8D/122n9 -+seQTXeshw8QjyCCT+CS3zBLqM8CxNGEasJmTrpJW+I1GwF3+7vbtuADawuF0ulV -VpHz3boKJAFJGij6cJFeBjojAl3kg4w4i7dXWMZW6fjC1e/GuaFA2wgKnxSnmyIL -9aIivi+v6fNfHgB0gVhF1zIU3gQxEACKSS0xvPIlbuq93zCXhxWzFrj5gGo5sdgB -BpsA9cevjR2Syb9c2rn2u49LBwA62moF53yp+q5C59YrOkBbuL+8WjZRMLEQmG4d -J67QrRHSSKpxB3NBgrv4J8ORhNwrkRtxWAU2UrSBe3fYfl2UAZMv2BFkhsPQ1xQW -3mzmboKKb5fdammS6qOloNCfKgqHYBbSRzklTKv0gDgZissPm+xWGW4+LUCqbroc -AjfoMBL3k8vgSvsxmP7Fcz8EfDzY6QoE/x4jgTO3TbePtq/aWC3f5nDL2pIEczOC -Kqp7XpvZbbnJGXr9E0Y3ayMpVVrAJTWFV5uJ3DC5G88RZL+RmwJYQLk8MchZCKND -O/hkuJVwmdm19rpidA05d/zRibHqxXZDobx2UhxwvQcwwcrJnEno1/3BjhUPDt7B -XiXVTBpTuuHVOPNjtBWwXCx5ky11keLfHZYa8ybl2eF+lrR6mPN0cMg9YGnGEWZ0 -9Hi/me1xqztf6sqY4tUTWhSGhlLewwIDAQABAoICAApRcP3jrEo5RLKgieIhWW7f -kZvQh4R4r8jMkZjOb5Gglz2jA/EF2bqnRmsWMh4q0N+envBVG5hYFRzIS2IP3BLi -VVk9vxY2P88x259dcqw2zs5GMR923kUpIWylQN+3BspOvMm08IuPhJTlhUE/wqJZ -7enIZQqI7vEofYgUNHeelgmjlJaSwGxNjpTAg6lflYDTZykf5DGOTGSzOeDyvW/J -muqyKTmioND2Eu3JetAFUa0MObP6fwbntytXCaDq+ix/yR9HICD2kAYX6CPtR1QU -kl6qrMZGultmMhGjr1zAArvZGmZCwQ26hERSL8qv1UtRNKegBGGViVJa5GtIQ2dT -UmTWmWu/5gyxKvvjuqYl8Dub2/ZT0iGAsA6hGyUr+vpgjcNEZqsYhiEiQPi0g1sM -XyszytqG1F7JzXYgVzcdFA9L+eLD+i4nKD18TYTYHFGRmxwQ+HzHnetgDQ2gqRbB -XwT4lp643oNLMGyL+T0cQ7i1Hpq7Ko0S2FeXzzFe9B33uXbDvc0usier5qx2tgxc -zfgSqJjahfo4LCxhxvBWOup3U/sXNgyMCctr1qjpwGwLek+H1keOyv7FO9O6OgI1 -v5ZPFsJV7mK1fDLM/8QLDpUcUNnhPUfzsBdxKrjLfnZ8MPNczgv1GPzb4jsLvewf -g6ps8oBwnZDQVa6dMuyRAoIBAQDnTKRUsTMmFo01o0k90C8SwwE2x7Wry8r6vIIf -PMni3ZAS+zWFnu1zg82+83QpdvskntWM2iXS7nimmkXClCCFMDU/hYA9EsZtGIv6 -+xA6gYF0Xd3Qf9QrvhixOxHj3ixNyCeee3/9XUYln3ZfEx8cgCwHjPSIm3rOKI2M -PFnuG9xJ513sy6YCDrCdtb661E6bmsaMcIhu6S7At0njwnoL9aB617TSds5tFEr8 -74EW3D9epN01uUQ9MgZSXbzdQ82IswLps4a/k4wfDFp4qKpx7zOsoTSjA9il3fgW -QLhBXxnzTYYTvwxIgaW//fyqEL3p6t9zuYcjbORcrj7v8xIvAoIBAQDAASGjsSCA -hn03DXrI/atoXEC0htVwPwp4HTI0Z1/rOS0IrFBcX3CWx90Dr/clePHQGPk1yOO7 -oM83zumwggIOymtDhlTcCa77yN9x9AZMW3qPMF+mvAouUzItnlMrOjvfEnIWziWC -UsylBiV4/I6tf0zpH8zFYPNXq98fpv+UXyJDTW+YGBc2b2BwZZA6RdtFalqvunM7 -M8FIH8vSYEMR0YC47L2ceBJY/U9EQpsc6vuS7+CoXOH/WRb5v1z+a5O9sHWp8Rdc -Oh67B6v2feUT9TwhGUVF0L+ktW389e3N+VzPvbvICvRsOvo6+bceCJTszhNno00s -87bPyelaHXutAoIBAFtJ6onqri9YMz96RMv6wLl88Zu3UsKNWn1/rTO7AEtj+xsi -vssQINO4r5mv6Kb86L5ZWhuPdeI8cK4AsYvMftFSZ5G8lRKFuH8Scx0Jviv5NSjC -a2uBKDJjgsdgcv0mkQHZ/5kTUT6kc60htMxtdZgAFmCch17rTprTcppor23E3Trl -8DInZkvllFuKgc6nQKc1fSustoxfyC4TqTwVY6oYtdAGFr4CWhK/MaGGvcJSB0jJ -dO1hQ8eLWOdlS8dgnVxYmsu2KXavO1x9ua9pkmwJZrG5pla4i+dbJjFSNebHLCzU -6hgdDTIIyWxvSCuvE+Wg57R7AxU+Qxs5Qmnd280CggEAex4+m+BwnvmeQTb7jPZc -e0bsltX+90L1S6AtGT1QXF0Fa5JS1Wi9oXH3Xu3u5LBxHqdk5gAzR5UOSxL69pvn -BeT2cw4oTBBJjFp6LW/0ufHO3RJ/w0LApIPkoSvs2MM2sQv67HSzyKWfZBJU5QfN -1aLTholFnStV3tnu8TT8nf+C0PVOoZCREe7JQElf+n3g5NoV3KkKSuQdBEqfP/9K -Apr8l5f23eaAnV+Q/IxZOmnTd50pycwFft95xBvZXatNyUzlpltaR2FdY0DAHAcO -ZYXTUMYLjYEV4mAUbyijnHhR80QOrW+Y2+3VlwuZSEDofhCGkOY+Dp0YlJU8dPSC -4QKCAQEA3qlwsjJT8Vv+Sx2sZySDNtey/00yjxe4TckdH8dWTC22G38/ppbazlp/ -YVqFUgteoo5FI60HKa0+jLKQZpCIH6JgpL3yq81Obl0wRkrSNePRTL1Ikiff8P2j -bowFpbIdJLgDco1opJpDgTOz2mB7HlHu6RyoKjiVrNA/EOoks1Uljxdth6h/5ctr -rLn8dnz2sTtwxcUsOpyFcFQ2qaWJvSg+bF7JPPzMrpQfCR1qVWa43Kl8KlcWSKaq -ITpglIBY+h3F2GygAAcnpfkXde381Iw89y7TFd2LxWQR98zhnbJWF2JmuuPDtVRv -+HYZkcyQcpDwfC+2NOWOU7NQj+IDIA== ------END PRIVATE KEY----- diff --git a/Tests/AsyncHTTPClientTests/ResponseDelayGetTests.swift b/Tests/AsyncHTTPClientTests/ResponseDelayGetTests.swift deleted file mode 100644 index 5fd1d6720..000000000 --- a/Tests/AsyncHTTPClientTests/ResponseDelayGetTests.swift +++ /dev/null @@ -1,46 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2022 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import AsyncHTTPClient -import Atomics -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOFoundationCompat -import NIOHTTP1 -import NIOHTTPCompression -import NIOPosix -import NIOSSL -import NIOTestUtils -import NIOTransportServices -import XCTest - -#if canImport(Network) -import Network -#endif - -final class ResponseDelayGetTests: XCTestCaseHTTPClientTestsBaseClass { - func testResponseDelayGet() throws { - let req = try HTTPClient.Request( - url: self.defaultHTTPBinURLPrefix + "get", - method: .GET, - headers: ["X-internal-delay": "2000"], - body: nil - ) - let start = NIODeadline.now() - let response = try self.defaultClient.execute(request: req).wait() - XCTAssertGreaterThanOrEqual(.now() - start, .milliseconds(1_900)) - XCTAssertEqual(response.status, .ok) - } -} diff --git a/Tests/AsyncHTTPClientTests/SOCKSEventsHandlerTests.swift b/Tests/AsyncHTTPClientTests/SOCKSEventsHandlerTests.swift deleted file mode 100644 index 2352c6c1c..000000000 --- a/Tests/AsyncHTTPClientTests/SOCKSEventsHandlerTests.swift +++ /dev/null @@ -1,91 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOEmbedded -import NIOSOCKS -import XCTest - -@testable import AsyncHTTPClient - -class SOCKSEventsHandlerTests: XCTestCase { - func testHandlerHappyPath() { - let socksEventsHandler = SOCKSEventsHandler(deadline: .now() + .seconds(10)) - XCTAssertNil(socksEventsHandler.socksEstablishedFuture) - let embedded = EmbeddedChannel(handlers: [socksEventsHandler]) - XCTAssertNotNil(socksEventsHandler.socksEstablishedFuture) - - XCTAssertNoThrow(try embedded.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait()) - - embedded.pipeline.fireUserInboundEventTriggered(SOCKSProxyEstablishedEvent()) - XCTAssertNoThrow(try XCTUnwrap(socksEventsHandler.socksEstablishedFuture).wait()) - } - - func testHandlerFailsFutureWhenRemovedWithoutEvent() { - let socksEventsHandler = SOCKSEventsHandler(deadline: .now() + .seconds(10)) - XCTAssertNil(socksEventsHandler.socksEstablishedFuture) - let embedded = EmbeddedChannel(handlers: [socksEventsHandler]) - XCTAssertNotNil(socksEventsHandler.socksEstablishedFuture) - - XCTAssertNoThrow(try embedded.pipeline.syncOperations.removeHandler(socksEventsHandler).wait()) - XCTAssertThrowsError(try XCTUnwrap(socksEventsHandler.socksEstablishedFuture).wait()) - } - - func testHandlerFailsFutureWhenHandshakeFails() { - let socksEventsHandler = SOCKSEventsHandler(deadline: .now() + .seconds(10)) - XCTAssertNil(socksEventsHandler.socksEstablishedFuture) - let embedded = EmbeddedChannel(handlers: [socksEventsHandler]) - XCTAssertNotNil(socksEventsHandler.socksEstablishedFuture) - - let error = SOCKSError.InvalidReservedByte(actual: 19) - embedded.pipeline.fireErrorCaught(error) - XCTAssertThrowsError(try XCTUnwrap(socksEventsHandler.socksEstablishedFuture).wait()) { - XCTAssertEqual($0 as? SOCKSError.InvalidReservedByte, error) - } - } - - func testHandlerClosesConnectionIfHandshakeTimesout() { - // .uptimeNanoseconds(0) => .now() for EmbeddedEventLoops - let socksEventsHandler = SOCKSEventsHandler(deadline: .uptimeNanoseconds(0) + .milliseconds(10)) - XCTAssertNil(socksEventsHandler.socksEstablishedFuture) - let embedded = EmbeddedChannel(handlers: [socksEventsHandler]) - XCTAssertNotNil(socksEventsHandler.socksEstablishedFuture) - - XCTAssertNoThrow(try embedded.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait()) - - embedded.embeddedEventLoop.advanceTime(by: .milliseconds(20)) - - XCTAssertThrowsError(try XCTUnwrap(socksEventsHandler.socksEstablishedFuture).wait()) { - XCTAssertEqual($0 as? HTTPClientError, .socksHandshakeTimeout) - } - XCTAssertFalse(embedded.isActive, "The timeout shall close the connection") - } - - func testHandlerWorksIfDeadlineIsInPast() { - // .uptimeNanoseconds(0) => .now() for EmbeddedEventLoops - let socksEventsHandler = SOCKSEventsHandler(deadline: .uptimeNanoseconds(0)) - XCTAssertNil(socksEventsHandler.socksEstablishedFuture) - let embedded = EmbeddedChannel(handlers: [socksEventsHandler]) - embedded.embeddedEventLoop.advanceTime(by: .milliseconds(10)) - - XCTAssertNoThrow(try embedded.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait()) - - // schedules execute only on the next tick - embedded.embeddedEventLoop.run() - XCTAssertThrowsError(try XCTUnwrap(socksEventsHandler.socksEstablishedFuture).wait()) { - XCTAssertEqual($0 as? HTTPClientError, .socksHandshakeTimeout) - } - XCTAssertFalse(embedded.isActive, "The timeout shall close the connection") - } -} diff --git a/Tests/AsyncHTTPClientTests/SOCKSTestUtils.swift b/Tests/AsyncHTTPClientTests/SOCKSTestUtils.swift deleted file mode 100644 index 50d26b278..000000000 --- a/Tests/AsyncHTTPClientTests/SOCKSTestUtils.swift +++ /dev/null @@ -1,181 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import AsyncHTTPClient -import NIOCore -import NIOHTTP1 -import NIOPosix -import NIOSOCKS -import XCTest - -struct MockSOCKSError: Error, Hashable { - var description: String -} - -class TestSOCKSBadServerHandler: ChannelInboundHandler { - typealias InboundIn = ByteBuffer - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - // just write some nonsense bytes - let buffer = context.channel.allocator.buffer(bytes: [0xAA, 0xBB, 0xCC, 0xDD, 0xEE]) - context.writeAndFlush(.init(buffer), promise: nil) - } -} - -class MockSOCKSServer { - let channel: Channel - - var port: Int { - self.channel.localAddress!.port! - } - - init( - expectedURL: String, - expectedResponse: String, - misbehave: Bool = false, - file: String = #filePath, - line: UInt = #line - ) throws { - let elg = MultiThreadedEventLoopGroup(numberOfThreads: 1) - let bootstrap: ServerBootstrap - if misbehave { - bootstrap = ServerBootstrap(group: elg) - .serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) - .childChannelInitializer { channel in - channel.eventLoop.makeCompletedFuture { - try channel.pipeline.syncOperations.addHandler(TestSOCKSBadServerHandler()) - } - } - } else { - bootstrap = ServerBootstrap(group: elg) - .serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) - .childChannelInitializer { channel in - channel.eventLoop.makeCompletedFuture { - let handshakeHandler = SOCKSServerHandshakeHandler() - try channel.pipeline.syncOperations.addHandlers([ - handshakeHandler, - SOCKSTestHandler(handshakeHandler: handshakeHandler), - TestHTTPServer( - expectedURL: expectedURL, - expectedResponse: expectedResponse, - file: file, - line: line - ), - ]) - } - } - } - self.channel = try bootstrap.bind(host: "localhost", port: 0).wait() - } - - func shutdown() throws { - try self.channel.close().wait() - } -} - -class SOCKSTestHandler: ChannelInboundHandler, RemovableChannelHandler { - typealias InboundIn = ClientMessage - - let handshakeHandler: SOCKSServerHandshakeHandler - - init(handshakeHandler: SOCKSServerHandshakeHandler) { - self.handshakeHandler = handshakeHandler - } - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - guard context.channel.isActive else { - return - } - - let message = self.unwrapInboundIn(data) - switch message { - case .greeting: - context.writeAndFlush( - .init( - ServerMessage.selectedAuthenticationMethod(.init(method: .noneRequired)) - ), - promise: nil - ) - case .authenticationData: - context.fireErrorCaught(MockSOCKSError(description: "Received authentication data but didn't receive any.")) - case .request(let request): - context.writeAndFlush( - .init( - ServerMessage.response(.init(reply: .succeeded, boundAddress: request.addressType)) - ), - promise: nil - ) - - do { - try context.channel.pipeline.syncOperations.addHandlers( - [ - ByteToMessageHandler(HTTPRequestDecoder()), - HTTPResponseEncoder(), - ], - position: .after(self) - ) - context.channel.pipeline.syncOperations.removeHandler(self, promise: nil) - context.channel.pipeline.syncOperations.removeHandler(self.handshakeHandler, promise: nil) - } catch { - context.fireErrorCaught(error) - } - } - } -} - -class TestHTTPServer: ChannelInboundHandler { - typealias InboundIn = HTTPServerRequestPart - typealias OutboundOut = HTTPServerResponsePart - - let expectedURL: String - let expectedResponse: String - let file: String - let line: UInt - var requestCount = 0 - - init(expectedURL: String, expectedResponse: String, file: String, line: UInt) { - self.expectedURL = expectedURL - self.expectedResponse = expectedResponse - self.file = file - self.line = line - } - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - let message = self.unwrapInboundIn(data) - switch message { - case .head(let head): - guard self.requestCount == 0 else { - return - } - XCTAssertEqual(head.uri, self.expectedURL) - self.requestCount += 1 - case .body: - break - case .end: - context.write(self.wrapOutboundOut(.head(.init(version: .http1_1, status: .ok))), promise: nil) - context.write( - self.wrapOutboundOut( - .body(.byteBuffer(context.channel.allocator.buffer(string: self.expectedResponse))) - ), - promise: nil - ) - context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: nil) - } - } - - func errorCaught(context: ChannelHandlerContext, error: Error) { - context.fireErrorCaught(error) - context.close(promise: nil) - } -} diff --git a/Tests/AsyncHTTPClientTests/SSLContextCacheTests.swift b/Tests/AsyncHTTPClientTests/SSLContextCacheTests.swift deleted file mode 100644 index c7588cc7d..000000000 --- a/Tests/AsyncHTTPClientTests/SSLContextCacheTests.swift +++ /dev/null @@ -1,103 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOPosix -import NIOSSL -import XCTest - -@testable import AsyncHTTPClient - -final class SSLContextCacheTests: XCTestCase { - func testRequestingSSLContextWorks() { - let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) - let eventLoop = group.next() - let cache = SSLContextCache() - defer { - XCTAssertNoThrow(try group.syncShutdownGracefully()) - } - - XCTAssertNoThrow( - try cache.sslContext( - tlsConfiguration: .makeClientConfiguration(), - eventLoop: eventLoop, - logger: HTTPClient.loggingDisabled - ).wait() - ) - } - - func testCacheWorks() { - let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) - let eventLoop = group.next() - let cache = SSLContextCache() - defer { - XCTAssertNoThrow(try group.syncShutdownGracefully()) - } - - var firstContext: NIOSSLContext? - var secondContext: NIOSSLContext? - - XCTAssertNoThrow( - firstContext = try cache.sslContext( - tlsConfiguration: .makeClientConfiguration(), - eventLoop: eventLoop, - logger: HTTPClient.loggingDisabled - ).wait() - ) - XCTAssertNoThrow( - secondContext = try cache.sslContext( - tlsConfiguration: .makeClientConfiguration(), - eventLoop: eventLoop, - logger: HTTPClient.loggingDisabled - ).wait() - ) - XCTAssertNotNil(firstContext) - XCTAssertNotNil(secondContext) - XCTAssert(firstContext === secondContext) - } - - func testCacheDoesNotReturnWrongEntry() { - let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) - let eventLoop = group.next() - let cache = SSLContextCache() - defer { - XCTAssertNoThrow(try group.syncShutdownGracefully()) - } - - var firstContext: NIOSSLContext? - var secondContext: NIOSSLContext? - - XCTAssertNoThrow( - firstContext = try cache.sslContext( - tlsConfiguration: .makeClientConfiguration(), - eventLoop: eventLoop, - logger: HTTPClient.loggingDisabled - ).wait() - ) - - // Second one has a _different_ TLSConfiguration. - var testTLSConfig = TLSConfiguration.makeClientConfiguration() - testTLSConfig.certificateVerification = .none - XCTAssertNoThrow( - secondContext = try cache.sslContext( - tlsConfiguration: testTLSConfig, - eventLoop: eventLoop, - logger: HTTPClient.loggingDisabled - ).wait() - ) - XCTAssertNotNil(firstContext) - XCTAssertNotNil(secondContext) - XCTAssert(firstContext !== secondContext) - } -} diff --git a/Tests/AsyncHTTPClientTests/StressGetHttpsTests.swift b/Tests/AsyncHTTPClientTests/StressGetHttpsTests.swift deleted file mode 100644 index 587e6c64c..000000000 --- a/Tests/AsyncHTTPClientTests/StressGetHttpsTests.swift +++ /dev/null @@ -1,58 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2022 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import AsyncHTTPClient -import Atomics -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOFoundationCompat -import NIOHTTP1 -import NIOHTTPCompression -import NIOPosix -import NIOSSL -import NIOTestUtils -import NIOTransportServices -import XCTest - -#if canImport(Network) -import Network -#endif - -final class StressGetHttpsTests: XCTestCaseHTTPClientTestsBaseClass { - func testStressGetHttps() throws { - let localHTTPBin = HTTPBin(.http1_1(ssl: true)) - let localClient = HTTPClient( - eventLoopGroupProvider: .shared(self.clientGroup), - configuration: HTTPClient.Configuration(certificateVerification: .none) - ) - defer { - XCTAssertNoThrow(try localClient.syncShutdown()) - XCTAssertNoThrow(try localHTTPBin.shutdown()) - } - - let eventLoop = localClient.eventLoopGroup.next() - let requestCount = 200 - var futureResults = [EventLoopFuture]() - for _ in 1...requestCount { - let req = try HTTPClient.Request( - url: "https://localhost/(localHTTPBin.port)/get", - method: .GET, - headers: ["X-internal-delay": "100"] - ) - futureResults.append(localClient.execute(request: req)) - } - XCTAssertNoThrow(try EventLoopFuture.andAllSucceed(futureResults, on: eventLoop).wait()) - } -} diff --git a/Tests/AsyncHTTPClientTests/TLSEventsHandlerTests.swift b/Tests/AsyncHTTPClientTests/TLSEventsHandlerTests.swift deleted file mode 100644 index 988ba6e3f..000000000 --- a/Tests/AsyncHTTPClientTests/TLSEventsHandlerTests.swift +++ /dev/null @@ -1,72 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOEmbedded -import NIOSSL -import NIOTLS -import XCTest - -@testable import AsyncHTTPClient - -class TLSEventsHandlerTests: XCTestCase { - func testHandlerHappyPath() { - let tlsEventsHandler = TLSEventsHandler(deadline: nil) - XCTAssertNil(tlsEventsHandler.tlsEstablishedFuture) - let embedded = EmbeddedChannel(handlers: [tlsEventsHandler]) - XCTAssertNotNil(tlsEventsHandler.tlsEstablishedFuture) - - XCTAssertNoThrow(try embedded.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait()) - - embedded.pipeline.fireUserInboundEventTriggered(TLSUserEvent.handshakeCompleted(negotiatedProtocol: "abcd1234")) - XCTAssertEqual(try XCTUnwrap(tlsEventsHandler.tlsEstablishedFuture).wait(), "abcd1234") - } - - func testHandlerFailsFutureWhenRemovedWithoutEvent() { - let tlsEventsHandler = TLSEventsHandler(deadline: nil) - XCTAssertNil(tlsEventsHandler.tlsEstablishedFuture) - let embedded = EmbeddedChannel(handlers: [tlsEventsHandler]) - XCTAssertNotNil(tlsEventsHandler.tlsEstablishedFuture) - - XCTAssertNoThrow(try embedded.pipeline.syncOperations.removeHandler(tlsEventsHandler).wait()) - XCTAssertThrowsError(try XCTUnwrap(tlsEventsHandler.tlsEstablishedFuture).wait()) - } - - func testHandlerFailsFutureWhenHandshakeFails() { - let tlsEventsHandler = TLSEventsHandler(deadline: nil) - XCTAssertNil(tlsEventsHandler.tlsEstablishedFuture) - let embedded = EmbeddedChannel(handlers: [tlsEventsHandler]) - XCTAssertNotNil(tlsEventsHandler.tlsEstablishedFuture) - - embedded.pipeline.fireErrorCaught(NIOSSLError.handshakeFailed(BoringSSLError.wantConnect)) - XCTAssertThrowsError(try XCTUnwrap(tlsEventsHandler.tlsEstablishedFuture).wait()) { - XCTAssertEqual($0 as? NIOSSLError, .handshakeFailed(BoringSSLError.wantConnect)) - } - } - - func testHandlerIgnoresShutdownCompletedEvent() { - let tlsEventsHandler = TLSEventsHandler(deadline: nil) - XCTAssertNil(tlsEventsHandler.tlsEstablishedFuture) - let embedded = EmbeddedChannel(handlers: [tlsEventsHandler]) - XCTAssertNotNil(tlsEventsHandler.tlsEstablishedFuture) - - XCTAssertNoThrow(try embedded.connect(to: .makeAddressResolvingHost("localhost", port: 0)).wait()) - - // ignore event - embedded.pipeline.fireUserInboundEventTriggered(TLSUserEvent.shutdownCompleted) - - embedded.pipeline.fireUserInboundEventTriggered(TLSUserEvent.handshakeCompleted(negotiatedProtocol: "alpn")) - XCTAssertEqual(try XCTUnwrap(tlsEventsHandler.tlsEstablishedFuture).wait(), "alpn") - } -} diff --git a/Tests/AsyncHTTPClientTests/Transaction+StateMachineTests.swift b/Tests/AsyncHTTPClientTests/Transaction+StateMachineTests.swift deleted file mode 100644 index a631e9a93..000000000 --- a/Tests/AsyncHTTPClientTests/Transaction+StateMachineTests.swift +++ /dev/null @@ -1,285 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import NIOCore -import NIOEmbedded -import NIOHTTP1 -import XCTest - -@testable import AsyncHTTPClient - -struct NoOpAsyncSequenceProducerDelegate: NIOAsyncSequenceProducerDelegate { - func produceMore() {} - func didTerminate() {} -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -final class Transaction_StateMachineTests: XCTestCase { - func testRequestWasQueuedAfterWillExecuteRequestWasCalled() { - let eventLoop = EmbeddedEventLoop() - XCTAsyncTest { - func workaround(_ continuation: CheckedContinuation) { - var state = Transaction.StateMachine(continuation) - let executor = MockRequestExecutor(eventLoop: eventLoop) - let queuer = MockTaskQueuer() - - XCTAssertEqual(state.willExecuteRequest(executor), .none) - state.requestWasQueued(queuer) - - let failAction = state.fail(HTTPClientError.cancelled) - guard - case .failResponseHead(_, let error, let scheduler, let rexecutor, let bodyStreamContinuation) = - failAction - else { - return XCTFail("Unexpected fail action: \(failAction)") - } - XCTAssertEqual(error as? HTTPClientError, .cancelled) - XCTAssertNil(scheduler) - XCTAssertNil(bodyStreamContinuation) - XCTAssert((rexecutor as? MockRequestExecutor) === executor) - - continuation.resume(throwing: HTTPClientError.cancelled) - } - - await XCTAssertThrowsError(try await withCheckedThrowingContinuation(workaround)) - } - } - - func testRequestBodyStreamWasPaused() { - let eventLoop = EmbeddedEventLoop() - XCTAsyncTest { - func workaround(_ continuation: CheckedContinuation) { - var state = Transaction.StateMachine(continuation) - let executor = MockRequestExecutor(eventLoop: eventLoop) - - XCTAssertEqual(state.willExecuteRequest(executor), .none) - XCTAssertEqual(state.resumeRequestBodyStream(), .startStream(ByteBufferAllocator())) - XCTAssertEqual(state.writeNextRequestPart(), .writeAndContinue(executor)) - state.pauseRequestBodyStream() - XCTAssertEqual(state.writeNextRequestPart(), .writeAndWait(executor)) - - continuation.resume(throwing: HTTPClientError.cancelled) - } - - await XCTAssertThrowsError(try await withCheckedThrowingContinuation(workaround)) - } - } - - func testQueuedRequestGetsRemovedWhenDeadlineExceeded() { - struct MyError: Error, Equatable {} - XCTAsyncTest { - func workaround(_ continuation: CheckedContinuation) { - var state = Transaction.StateMachine(continuation) - let queuer = MockTaskQueuer() - - state.requestWasQueued(queuer) - - let deadlineExceededAction = state.deadlineExceeded() - guard case .cancelSchedulerOnly(let scheduler) = deadlineExceededAction else { - return XCTFail("Unexpected fail action: \(deadlineExceededAction)") - } - XCTAssertIdentical(scheduler as? MockTaskQueuer, queuer) - - let failAction = state.fail(MyError()) - guard - case .failResponseHead(let continuation, let error, nil, nil, bodyStreamContinuation: nil) = - failAction - else { - return XCTFail("Unexpected fail action: \(failAction)") - } - XCTAssertIdentical(scheduler as? MockTaskQueuer, queuer) - - continuation.resume(throwing: error) - } - - await XCTAssertThrowsError(try await withCheckedThrowingContinuation(workaround)) { - XCTAssertEqualTypeAndValue($0, MyError()) - } - } - } - - func testDeadlineExceededAndFullyFailedRequestCanBeCanceledWithNoEffect() { - struct MyError: Error, Equatable {} - XCTAsyncTest { - func workaround(_ continuation: CheckedContinuation) { - var state = Transaction.StateMachine(continuation) - let queuer = MockTaskQueuer() - - state.requestWasQueued(queuer) - - let deadlineExceededAction = state.deadlineExceeded() - guard case .cancelSchedulerOnly(let scheduler) = deadlineExceededAction else { - return XCTFail("Unexpected fail action: \(deadlineExceededAction)") - } - XCTAssertIdentical(scheduler as? MockTaskQueuer, queuer) - - let failAction = state.fail(MyError()) - guard - case .failResponseHead(let continuation, let error, nil, nil, bodyStreamContinuation: nil) = - failAction - else { - return XCTFail("Unexpected fail action: \(failAction)") - } - XCTAssertIdentical(scheduler as? MockTaskQueuer, queuer) - - let secondFailAction = state.fail(HTTPClientError.cancelled) - guard case .none = secondFailAction else { - return XCTFail("Unexpected fail action: \(secondFailAction)") - } - - continuation.resume(throwing: error) - } - - await XCTAssertThrowsError(try await withCheckedThrowingContinuation(workaround)) { - XCTAssertEqualTypeAndValue($0, MyError()) - } - } - } - - func testScheduledRequestGetsRemovedWhenDeadlineExceeded() { - let eventLoop = EmbeddedEventLoop() - XCTAsyncTest { - func workaround(_ continuation: CheckedContinuation) { - var state = Transaction.StateMachine(continuation) - let executor = MockRequestExecutor(eventLoop: eventLoop) - let queuer = MockTaskQueuer() - - XCTAssertEqual(state.willExecuteRequest(executor), .none) - state.requestWasQueued(queuer) - - let failAction = state.deadlineExceeded() - guard case .cancel(let continuation, nil, let rexecutor, nil) = failAction else { - return XCTFail("Unexpected fail action: \(failAction)") - } - XCTAssertIdentical(rexecutor as? MockRequestExecutor, executor) - - continuation.resume(throwing: HTTPClientError.deadlineExceeded) - } - - await XCTAssertThrowsError(try await withCheckedThrowingContinuation(workaround)) - } - } - - func testDeadlineExceededRaceWithRequestWillExecute() { - let eventLoop = EmbeddedEventLoop() - XCTAsyncTest { - func workaround(_ continuation: CheckedContinuation) { - var state = Transaction.StateMachine(continuation) - let expectedExecutor = MockRequestExecutor(eventLoop: eventLoop) - let queuer = MockTaskQueuer() - - state.requestWasQueued(queuer) - - let deadlineExceededAction = state.deadlineExceeded() - guard case .cancelSchedulerOnly(let scheduler) = deadlineExceededAction else { - return XCTFail("Unexpected fail action: \(deadlineExceededAction)") - } - XCTAssertIdentical(scheduler as? MockTaskQueuer, queuer) - - let failAction = state.willExecuteRequest(expectedExecutor) - guard case .cancelAndFail(let returnedExecutor, let continuation, with: let error) = failAction else { - return XCTFail("Unexpected fail action: \(failAction)") - } - XCTAssertIdentical(returnedExecutor as? MockRequestExecutor, expectedExecutor) - - continuation.resume(throwing: error) - } - - await XCTAssertThrowsError(try await withCheckedThrowingContinuation(workaround)) { - XCTAssertEqualTypeAndValue($0, HTTPClientError.deadlineExceeded) - } - } - } - - func testRequestWithHeadReceivedGetNotCancelledWhenDeadlineExceeded() { - let eventLoop = EmbeddedEventLoop() - XCTAsyncTest { - func workaround(_ continuation: CheckedContinuation) { - var state = Transaction.StateMachine(continuation) - let executor = MockRequestExecutor(eventLoop: eventLoop) - let queuer = MockTaskQueuer() - - XCTAssertEqual(state.willExecuteRequest(executor), .none) - state.requestWasQueued(queuer) - let head = HTTPResponseHead(version: .http1_1, status: .ok) - let receiveResponseHeadAction = state.receiveResponseHead( - head, - delegate: NoOpAsyncSequenceProducerDelegate() - ) - guard case .succeedResponseHead(_, let continuation) = receiveResponseHeadAction else { - return XCTFail("Unexpected action: \(receiveResponseHeadAction)") - } - - let failAction = state.deadlineExceeded() - guard case .none = failAction else { - return XCTFail("Unexpected fail action: \(failAction)") - } - continuation.resume(throwing: HTTPClientError.deadlineExceeded) - } - - await XCTAssertThrowsError(try await withCheckedThrowingContinuation(workaround)) - } - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension Transaction.StateMachine.StartExecutionAction: Equatable { - public static func == (lhs: Self, rhs: Self) -> Bool { - switch (lhs, rhs) { - case (.none, .none): - return true - case (.cancel(let lhsEx), .cancel(let rhsEx)): - if let lhsMock = lhsEx as? MockRequestExecutor, let rhsMock = rhsEx as? MockRequestExecutor { - return lhsMock === rhsMock - } - return false - default: - return false - } - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension Transaction.StateMachine.ResumeProducingAction: Equatable { - public static func == (lhs: Self, rhs: Self) -> Bool { - switch (lhs, rhs) { - case (.none, .none): - return true - case (.resumeStream, .resumeStream): - return true - case (.startStream, .startStream): - return true - default: - return false - } - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension Transaction.StateMachine.NextWriteAction: Equatable { - public static func == (lhs: Self, rhs: Self) -> Bool { - switch (lhs, rhs) { - case (.writeAndWait(let lhsEx), .writeAndWait(let rhsEx)), - (.writeAndContinue(let lhsEx), .writeAndContinue(let rhsEx)): - if let lhsMock = lhsEx as? MockRequestExecutor, let rhsMock = rhsEx as? MockRequestExecutor { - return lhsMock === rhsMock - } - return false - case (.fail, .fail): - return true - default: - return false - } - } -} diff --git a/Tests/AsyncHTTPClientTests/TransactionTests.swift b/Tests/AsyncHTTPClientTests/TransactionTests.swift deleted file mode 100644 index 3316de370..000000000 --- a/Tests/AsyncHTTPClientTests/TransactionTests.swift +++ /dev/null @@ -1,724 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Logging -import NIOConcurrencyHelpers -import NIOCore -import NIOEmbedded -import NIOFoundationCompat -import NIOHTTP1 -import NIOPosix -import XCTest - -@testable import AsyncHTTPClient - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -typealias PreparedRequest = HTTPClientRequest.Prepared - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -final class TransactionTests: XCTestCase { - func testCancelAsyncRequest() { - XCTAsyncTest { - let loop = NIOAsyncTestingEventLoop() - let scheduledRequestCanceled = loop.makePromise(of: Void.self) - defer { XCTAssertNoThrow(try loop.syncShutdownGracefully()) } - - var request = HTTPClientRequest(url: "https://localhost/") - request.method = .GET - var maybePreparedRequest: PreparedRequest? - XCTAssertNoThrow(maybePreparedRequest = try PreparedRequest(request)) - guard let preparedRequest = maybePreparedRequest else { - return XCTFail("Expected to have a request here.") - } - let (transaction, responseTask) = await Transaction.makeWithResultTask( - request: preparedRequest, - preferredEventLoop: loop - ) - - let queuer = MockTaskQueuer { _ in - scheduledRequestCanceled.succeed() - } - transaction.requestWasQueued(queuer) - - XCTAssertEqual(queuer.hitCancelCount, 0) - Task.detached { - try await Task.sleep(nanoseconds: 5 * 1000 * 1000) - transaction.cancel() - } - - await XCTAssertThrowsError(try await responseTask.value) { error in - XCTAssertTrue(error is CancellationError, "unexpected error \(error)") - } - - // self.fulfillment(of:) is not available on Linux - try await scheduledRequestCanceled.futureResult.timeout(after: .seconds(1)).get() - } - } - - func testDeadlineExceededWhileQueuedAndExecutorImmediatelyCancelsTask() { - XCTAsyncTest { - let loop = NIOAsyncTestingEventLoop() - defer { XCTAssertNoThrow(try loop.syncShutdownGracefully()) } - - var request = HTTPClientRequest(url: "https://localhost/") - request.method = .GET - var maybePreparedRequest: PreparedRequest? - XCTAssertNoThrow(maybePreparedRequest = try PreparedRequest(request)) - guard let preparedRequest = maybePreparedRequest else { - return XCTFail("Expected to have a request here.") - } - let (transaction, responseTask) = await Transaction.makeWithResultTask( - request: preparedRequest, - preferredEventLoop: loop - ) - - let queuer = MockTaskQueuer() - transaction.requestWasQueued(queuer) - - transaction.deadlineExceeded() - - struct Executor: HTTPRequestExecutor { - func writeRequestBodyPart( - _: NIOCore.IOData, - request: AsyncHTTPClient.HTTPExecutableRequest, - promise: NIOCore.EventLoopPromise? - ) { - XCTFail() - } - - func finishRequestBodyStream( - _ task: AsyncHTTPClient.HTTPExecutableRequest, - promise: NIOCore.EventLoopPromise? - ) { - XCTFail() - } - - func demandResponseBodyStream(_: AsyncHTTPClient.HTTPExecutableRequest) { - XCTFail() - } - - func cancelRequest(_ task: AsyncHTTPClient.HTTPExecutableRequest) { - task.fail(HTTPClientError.cancelled) - } - } - - transaction.willExecuteRequest(Executor()) - - await XCTAssertThrowsError(try await responseTask.value) { error in - XCTAssertEqualTypeAndValue(error, HTTPClientError.deadlineExceeded) - } - } - } - - func testResponseStreamingWorks() { - XCTAsyncTest { - let loop = NIOAsyncTestingEventLoop() - defer { XCTAssertNoThrow(try loop.syncShutdownGracefully()) } - - var request = HTTPClientRequest(url: "https://localhost/") - request.method = .GET - - var maybePreparedRequest: PreparedRequest? - XCTAssertNoThrow(maybePreparedRequest = try PreparedRequest(request)) - guard let preparedRequest = maybePreparedRequest else { - return - } - let (transaction, responseTask) = await Transaction.makeWithResultTask( - request: preparedRequest, - preferredEventLoop: loop - ) - - let executor = MockRequestExecutor( - pauseRequestBodyPartStreamAfterASingleWrite: true, - eventLoop: loop - ) - - transaction.willExecuteRequest(executor) - transaction.requestHeadSent() - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok, headers: ["foo": "bar"]) - XCTAssertFalse(executor.signalledDemandForResponseBody) - transaction.receiveResponseHead(responseHead) - - let response = try await responseTask.value - XCTAssertEqual(response.status, responseHead.status) - XCTAssertEqual(response.headers, responseHead.headers) - XCTAssertEqual(response.version, responseHead.version) - - let iterator = SharedIterator(response.body.filter { $0.readableBytes > 0 }) - - XCTAssertFalse(executor.signalledDemandForResponseBody, "Demand was not signalled yet.") - - for i in 0..<100 { - async let part = iterator.next() - - XCTAssertNoThrow(try executor.receiveResponseDemand()) - executor.resetResponseStreamDemandSignal() - transaction.receiveResponseBodyParts([ByteBuffer(integer: i)]) - - let result = try await part - XCTAssertEqual(result, ByteBuffer(integer: i)) - } - - async let part = iterator.next() - XCTAssertNoThrow(try executor.receiveResponseDemand()) - executor.resetResponseStreamDemandSignal() - transaction.succeedRequest([]) - let result = try await part - XCTAssertNil(result) - } - } - - func testIgnoringResponseBodyWorks() { - XCTAsyncTest { - let loop = NIOAsyncTestingEventLoop() - defer { XCTAssertNoThrow(try loop.syncShutdownGracefully()) } - - var request = HTTPClientRequest(url: "https://localhost/") - request.method = .GET - - var maybePreparedRequest: PreparedRequest? - XCTAssertNoThrow(maybePreparedRequest = try PreparedRequest(request)) - guard let preparedRequest = maybePreparedRequest else { - return - } - var tuple: (Transaction, Task)! = await Transaction.makeWithResultTask( - request: preparedRequest, - preferredEventLoop: loop - ) - - let transaction = tuple.0 - var responseTask: Task! = tuple.1 - tuple = nil - - let executor = MockRequestExecutor( - pauseRequestBodyPartStreamAfterASingleWrite: true, - eventLoop: loop - ) - executor.runRequest(transaction) - await loop.run() - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok, headers: ["foo": "bar"]) - XCTAssertFalse(executor.signalledDemandForResponseBody) - transaction.receiveResponseHead(responseHead) - - var response: HTTPClientResponse? = try await responseTask.value - responseTask = nil - XCTAssertEqual(response?.status, responseHead.status) - XCTAssertEqual(response?.headers, responseHead.headers) - XCTAssertEqual(response?.version, responseHead.version) - response = nil - - XCTAssertFalse(executor.signalledDemandForResponseBody) - XCTAssertNoThrow(try executor.receiveCancellation()) - - // doesn't crash if receives more data because of race - transaction.receiveResponseBodyParts([ByteBuffer(string: "foo bar")]) - transaction.succeedRequest(nil) - } - } - - func testWriteBackpressureWorks() { - XCTAsyncTest { - let loop = NIOAsyncTestingEventLoop() - defer { XCTAssertNoThrow(try loop.syncShutdownGracefully()) } - - let streamWriter = AsyncSequenceWriter() - XCTAssertFalse(streamWriter.hasDemand, "Did not expect to have a demand at this point") - - var request = HTTPClientRequest(url: "https://localhost/") - request.method = .POST - request.body = .stream(streamWriter, length: .unknown) - - var maybePreparedRequest: PreparedRequest? - XCTAssertNoThrow(maybePreparedRequest = try PreparedRequest(request)) - guard let preparedRequest = maybePreparedRequest else { - return XCTFail("Expected to have a request here.") - } - let (transaction, responseTask) = await Transaction.makeWithResultTask( - request: preparedRequest, - preferredEventLoop: loop - ) - - let executor = MockRequestExecutor(eventLoop: loop) - - executor.runRequest(transaction) - await loop.run() - - for i in 0..<100 { - XCTAssertFalse(streamWriter.hasDemand, "Did not expect to have demand yet") - - transaction.resumeRequestBodyStream() - await streamWriter.demand() // wait's for the stream writer to signal demand - transaction.pauseRequestBodyStream() - - let part = ByteBuffer(integer: i) - streamWriter.write(part) - - XCTAssertNoThrow( - try executor.receiveRequestBody { - XCTAssertEqual($0, part) - } - ) - } - - transaction.resumeRequestBodyStream() - await streamWriter.demand() - - streamWriter.end() - XCTAssertNoThrow(try executor.receiveEndOfStream()) - - // write response! - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok, headers: ["foo": "bar"]) - XCTAssertFalse(executor.signalledDemandForResponseBody) - transaction.receiveResponseHead(responseHead) - - let response = try await responseTask.result.get() - XCTAssertEqual(response.status, responseHead.status) - XCTAssertEqual(response.headers, responseHead.headers) - XCTAssertEqual(response.version, responseHead.version) - - let iterator = SharedIterator(response.body) - - XCTAssertFalse(executor.signalledDemandForResponseBody, "Demand was not signalled yet.") - async let part = iterator.next() - - XCTAssertNoThrow(try executor.receiveResponseDemand()) - executor.resetResponseStreamDemandSignal() - transaction.succeedRequest([]) - let result = try await part - XCTAssertNil(result) - } - } - - func testSimpleGetRequest() { - XCTAsyncTest { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - let eventLoop = eventLoopGroup.next() - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let httpBin = HTTPBin(.http2(compress: false)) - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let connectionCreator = TestConnectionCreator() - let delegate = TestHTTP2ConnectionDelegate() - var maybeHTTP2Connection: HTTP2Connection.SendableView? - XCTAssertNoThrow( - maybeHTTP2Connection = try connectionCreator.createHTTP2Connection( - to: httpBin.port, - delegate: delegate, - on: eventLoop - ) - ) - guard let http2Connection = maybeHTTP2Connection else { - return XCTFail("Expected to have an HTTP2 connection here.") - } - - var request = HTTPClientRequest(url: "https://localhost/(httpBin.port)/") - request.headers = ["host": "localhost:\(httpBin.port)"] - - var maybePreparedRequest: PreparedRequest? - XCTAssertNoThrow(maybePreparedRequest = try PreparedRequest(request)) - guard let preparedRequest = maybePreparedRequest else { - return XCTFail("Expected to have a request here.") - } - let (transaction, responseTask) = await Transaction.makeWithResultTask( - request: preparedRequest, - preferredEventLoop: eventLoopGroup.next() - ) - - http2Connection.executeRequest(transaction) - - XCTAssertEqual(delegate.hitStreamClosed, 0) - - let response = try await responseTask.result.get() - - XCTAssertEqual(response.status, .ok) - XCTAssertEqual(response.version, .http2) - XCTAssertEqual(delegate.hitStreamClosed, 1) - - var body = try await response.body.reduce(into: ByteBuffer()) { partialResult, next in - var next = next - partialResult.writeBuffer(&next) - } - XCTAssertEqual( - try body.readJSONDecodable(RequestInfo.self, length: body.readableBytes), - RequestInfo(data: "", requestNumber: 1, connectionNumber: 0) - ) - } - } - - func testSimplePostRequest() { - XCTAsyncTest { - let loop = NIOAsyncTestingEventLoop() - defer { XCTAssertNoThrow(try loop.syncShutdownGracefully()) } - - var request = HTTPClientRequest(url: "https://localhost/") - request.method = .POST - request.body = .bytes("Hello world!".utf8, length: .unknown) - var maybePreparedRequest: PreparedRequest? - XCTAssertNoThrow(maybePreparedRequest = try PreparedRequest(request)) - guard let preparedRequest = maybePreparedRequest else { - return XCTFail("Expected to have a request here.") - } - let (transaction, responseTask) = await Transaction.makeWithResultTask( - request: preparedRequest, - preferredEventLoop: loop - ) - - let executor = MockRequestExecutor(eventLoop: loop) - executor.runRequest(transaction) - await loop.run() - executor.resumeRequestBodyStream() - XCTAssertNoThrow( - try executor.receiveRequestBody { - XCTAssertEqual($0.getString(at: 0, length: $0.readableBytes), "Hello world!") - } - ) - XCTAssertNoThrow(try executor.receiveEndOfStream()) - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok, headers: ["foo": "bar"]) - transaction.receiveResponseHead(responseHead) - transaction.succeedRequest(nil) - - let response = try await responseTask.value - XCTAssertEqual(response.status, .ok) - XCTAssertEqual(response.version, .http1_1) - XCTAssertEqual(response.headers, ["foo": "bar"]) - } - } - - func testPostStreamFails() { - XCTAsyncTest { - let loop = NIOAsyncTestingEventLoop() - defer { XCTAssertNoThrow(try loop.syncShutdownGracefully()) } - - let writer = AsyncSequenceWriter() - - var request = HTTPClientRequest(url: "https://localhost/") - request.method = .POST - request.body = .stream(writer, length: .unknown) - var maybePreparedRequest: PreparedRequest? - XCTAssertNoThrow(maybePreparedRequest = try PreparedRequest(request)) - guard let preparedRequest = maybePreparedRequest else { - return XCTFail("Expected to have a request here.") - } - let (transaction, responseTask) = await Transaction.makeWithResultTask( - request: preparedRequest, - preferredEventLoop: loop - ) - - let executor = MockRequestExecutor(eventLoop: loop) - executor.runRequest(transaction) - await loop.run() - executor.resumeRequestBodyStream() - - await writer.demand() - writer.write(.init(string: "Hello world!")) - - XCTAssertNoThrow( - try executor.receiveRequestBody { - XCTAssertEqual($0.getString(at: 0, length: $0.readableBytes), "Hello world!") - } - ) - - XCTAssertFalse(executor.isCancelled) - struct WriteError: Error, Equatable {} - writer.fail(WriteError()) - - await XCTAssertThrowsError(try await responseTask.value) { - XCTAssertEqual($0 as? WriteError, .init()) - } - XCTAssertNoThrow(try executor.receiveCancellation()) - } - } - - func testResponseStreamFails() { - XCTAsyncTest(timeout: 30) { - let loop = NIOAsyncTestingEventLoop() - defer { XCTAssertNoThrow(try loop.syncShutdownGracefully()) } - - var request = HTTPClientRequest(url: "https://localhost/") - request.method = .GET - - var maybePreparedRequest: PreparedRequest? - XCTAssertNoThrow(maybePreparedRequest = try PreparedRequest(request)) - guard let preparedRequest = maybePreparedRequest else { - return - } - let (transaction, responseTask) = await Transaction.makeWithResultTask( - request: preparedRequest, - preferredEventLoop: loop - ) - - let executor = MockRequestExecutor( - pauseRequestBodyPartStreamAfterASingleWrite: true, - eventLoop: loop - ) - - transaction.willExecuteRequest(executor) - transaction.requestHeadSent() - - let responseHead = HTTPResponseHead(version: .http1_1, status: .ok, headers: ["foo": "bar"]) - XCTAssertFalse(executor.signalledDemandForResponseBody) - transaction.receiveResponseHead(responseHead) - - let response = try await responseTask.value - - XCTAssertEqual(response.status, responseHead.status) - XCTAssertEqual(response.headers, responseHead.headers) - XCTAssertEqual(response.version, responseHead.version) - - XCTAssertFalse(executor.signalledDemandForResponseBody, "Demand was not signalled yet.") - let iterator = SharedIterator(response.body.filter { $0.readableBytes > 0 }) - - async let part1 = iterator.next() - XCTAssertNoThrow(try executor.receiveResponseDemand()) - executor.resetResponseStreamDemandSignal() - transaction.receiveResponseBodyParts([ByteBuffer(integer: 123)]) - - let result = try await part1 - XCTAssertEqual(result, ByteBuffer(integer: 123)) - - let responsePartTask = Task { - try await iterator.next() - } - XCTAssertNoThrow(try executor.receiveResponseDemand()) - executor.resetResponseStreamDemandSignal() - transaction.fail(HTTPClientError.readTimeout) - - // can't use XCTAssertThrowsError() here, since capturing async let variables is - // not allowed. - await XCTAssertThrowsError(try await responsePartTask.value) { - XCTAssertEqual($0 as? HTTPClientError, .readTimeout) - } - } - } - - func testBiDirectionalStreamingHTTP2() { - XCTAsyncTest { - let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) - let eventLoop = eventLoopGroup.next() - defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - - let httpBin = HTTPBin(.http2(compress: false)) { _ in HTTPEchoHandler() } - defer { XCTAssertNoThrow(try httpBin.shutdown()) } - - let connectionCreator = TestConnectionCreator() - let delegate = TestHTTP2ConnectionDelegate() - var maybeHTTP2Connection: HTTP2Connection.SendableView? - XCTAssertNoThrow( - maybeHTTP2Connection = try connectionCreator.createHTTP2Connection( - to: httpBin.port, - delegate: delegate, - on: eventLoop - ) - ) - guard let http2Connection = maybeHTTP2Connection else { - return XCTFail("Expected to have an HTTP2 connection here.") - } - - let streamWriter = AsyncSequenceWriter() - XCTAssertFalse(streamWriter.hasDemand, "Did not expect to have a demand at this point") - - var request = HTTPClientRequest(url: "https://localhost/(httpBin.port)/") - request.method = .POST - request.headers = ["host": "localhost:\(httpBin.port)"] - request.body = .stream(streamWriter, length: .known(Int64(800))) - - var maybePreparedRequest: PreparedRequest? - XCTAssertNoThrow(maybePreparedRequest = try PreparedRequest(request)) - guard let preparedRequest = maybePreparedRequest else { - return - } - let (transaction, responseTask) = await Transaction.makeWithResultTask( - request: preparedRequest, - preferredEventLoop: eventLoopGroup.next() - ) - - http2Connection.executeRequest(transaction) - - XCTAssertEqual(delegate.hitStreamClosed, 0) - - let response = try await responseTask.result.get() - - XCTAssertEqual(response.status, .ok) - XCTAssertEqual(response.version, .http2) - XCTAssertEqual(delegate.hitStreamClosed, 0) - - let iterator = SharedIterator(response.body.filter { $0.readableBytes > 0 }) - - // at this point we can start to write to the stream and wait for the results - - for i in 0..<100 { - let buffer = ByteBuffer(integer: i) - streamWriter.write(buffer) - var echoedBuffer = try await iterator.next() - guard let echoedInt = echoedBuffer?.readInteger(as: Int.self) else { - XCTFail("Expected to not be finished at this point") - break - } - XCTAssertEqual(i, echoedInt) - } - - XCTAssertEqual(delegate.hitStreamClosed, 0) - streamWriter.end() - let final = try await iterator.next() - XCTAssertNil(final) - XCTAssertEqual(delegate.hitStreamClosed, 1) - } - } -} - -// This needs a small explanation. If an iterator is a struct, it can't be used across multiple -// tasks. Since we want to wait for things to happen in tests, we need to `async let`, which creates -// implicit tasks. Therefore we need to wrap our iterator struct. -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -final class SharedIterator: Sendable where Wrapped.Element: Sendable { - private struct State: @unchecked Sendable { - var wrappedIterator: Wrapped.AsyncIterator - var nextCallInProgress: Bool = false - } - - private let state: NIOLockedValueBox - - init(_ sequence: Wrapped) { - self.state = NIOLockedValueBox(State(wrappedIterator: sequence.makeAsyncIterator())) - } - - func next() async throws -> Wrapped.Element? { - var iter = self.state.withLockedValue { - precondition($0.nextCallInProgress == false) - $0.nextCallInProgress = true - return $0.wrappedIterator - } - - defer { - self.state.withLockedValue { - precondition($0.nextCallInProgress == true) - $0.nextCallInProgress = false - $0.wrappedIterator = iter - } - } - return try await iter.next() - } -} - -/// non fail-able promise that only supports one observer -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -private actor Promise { - private enum State { - case initialised - case fulfilled(Value) - } - - private var state: State = .initialised - - private var observer: CheckedContinuation? - - init() {} - - func fulfil(_ value: Value) { - switch self.state { - case .initialised: - self.state = .fulfilled(value) - self.observer?.resume(returning: value) - case .fulfilled: - preconditionFailure("\(Self.self) over fulfilled") - } - } - - var value: Value { - get async { - switch self.state { - case .initialised: - return await withCheckedContinuation { (continuation: CheckedContinuation) in - precondition(self.observer == nil, "\(Self.self) supports only one observer") - self.observer = continuation - } - case .fulfilled(let value): - return value - } - } - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension Transaction { - #if compiler(>=6.0) - fileprivate static func makeWithResultTask( - request: sending PreparedRequest, - requestOptions: RequestOptions = .forTests(), - logger: Logger = Logger(label: "test"), - connectionDeadline: NIODeadline = .distantFuture, - preferredEventLoop: EventLoop - ) async -> (Transaction, _Concurrency.Task) { - let transactionPromise = Promise() - let task = Task { - try await withCheckedThrowingContinuation { - (continuation: CheckedContinuation) in - let transaction = Transaction( - request: request, - requestOptions: requestOptions, - logger: logger, - connectionDeadline: connectionDeadline, - preferredEventLoop: preferredEventLoop, - responseContinuation: continuation - ) - Task { - await transactionPromise.fulfil(transaction) - } - } - } - - return (await transactionPromise.value, task) - } - #else - fileprivate static func makeWithResultTask( - request: PreparedRequest, - requestOptions: RequestOptions = .forTests(), - logger: Logger = Logger(label: "test"), - connectionDeadline: NIODeadline = .distantFuture, - preferredEventLoop: EventLoop - ) async -> (Transaction, _Concurrency.Task) { - // It isn't sendable ... but on 6.0 and later we use 'sending'. - struct UnsafePrepareRequest: @unchecked Sendable { - var value: PreparedRequest - } - - let transactionPromise = Promise() - let unsafe = UnsafePrepareRequest(value: request) - let task = Task { - try await withCheckedThrowingContinuation { - (continuation: CheckedContinuation) in - let request = unsafe.value - let transaction = Transaction( - request: request, - requestOptions: requestOptions, - logger: logger, - connectionDeadline: connectionDeadline, - preferredEventLoop: preferredEventLoop, - responseContinuation: continuation - ) - Task { - await transactionPromise.fulfil(transaction) - } - } - } - - return (await transactionPromise.value, task) - } - #endif -} diff --git a/Tests/AsyncHTTPClientTests/XCTest+AsyncAwait.swift b/Tests/AsyncHTTPClientTests/XCTest+AsyncAwait.swift deleted file mode 100644 index 6cdcf4f8a..000000000 --- a/Tests/AsyncHTTPClientTests/XCTest+AsyncAwait.swift +++ /dev/null @@ -1,91 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the AsyncHTTPClient open source project -// -// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// -// -// Copyright 2021, gRPC Authors All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import XCTest - -extension XCTestCase { - /// Cross-platform XCTest support for async-await tests. - /// - /// Currently the Linux implementation of XCTest doesn't have async-await support. - /// Until it does, we make use of this shim which uses a detached `Task` along with - /// `XCTest.wait(for:timeout:)` to wrap the operation. - /// - /// - NOTE: Support for Linux is tracked by https://bugs.swift.org/browse/SR-14403. - /// - NOTE: Implementation currently in progress: https://github.com/apple/swift-corelibs-xctest/pull/326 - @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) - func XCTAsyncTest( - expectationDescription: String = "Async operation", - timeout: TimeInterval = 30, - file: StaticString = #filePath, - line: UInt = #line, - function: StaticString = #function, - operation: @escaping @Sendable () async throws -> Void - ) { - let expectation = self.expectation(description: expectationDescription) - Task { - do { - try await operation() - } catch { - XCTFail("Error thrown while executing \(function): \(error)", file: file, line: line) - for symbol in Thread.callStackSymbols { print(symbol) } - } - expectation.fulfill() - } - self.wait(for: [expectation], timeout: timeout) - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -internal func XCTAssertThrowsError( - _ expression: @autoclosure () async throws -> T, - verify: (Error) -> Void = { _ in }, - file: StaticString = #filePath, - line: UInt = #line -) async { - do { - _ = try await expression() - XCTFail("Expression did not throw error", file: file, line: line) - } catch { - verify(error) - } -} - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -internal func XCTAssertNoThrowWithResult( - _ expression: @autoclosure () async throws -> Result, - file: StaticString = #filePath, - line: UInt = #line -) async -> Result? { - do { - return try await expression() - } catch { - XCTFail("Expression did throw: \(error)", file: file, line: line) - } - return nil -} diff --git a/dev/git.commit.template b/dev/git.commit.template deleted file mode 100644 index c52bfa1ad..000000000 --- a/dev/git.commit.template +++ /dev/null @@ -1,14 +0,0 @@ -One line description of your change - -Motivation: - -Explain here the context, and why you're making that change. -What is the problem you're trying to solve. - -Modifications: - -Describe the modifications you've done. - -Result: - -After your change, what will change. diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes.html b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes.html new file mode 100644 index 000000000..cec4d91ef --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes.html @@ -0,0 +1,230 @@ + + + + Classes Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+

Classes

+

The following classes are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + HTTPClient + +
    +
    +
    +
    +
    +
    +

    HTTPClient class provides API for request execution.

    + +

    Example:

    +
        let client = HTTPClient(eventLoopGroupProvider = .createNew)
    +    client.get(url: "https://swift.org/", deadline: .now() + .seconds(1)).whenComplete { result in
    +        switch result {
    +        case .failure(let error):
    +            // process error
    +        case .success(let response):
    +            if let response.status == .ok {
    +                // handle response
    +            } else {
    +                // handle remote error
    +            }
    +        }
    +    }
    +
    + +

    It is important to close the client instance, for example in a defer statement, after use to cleanly shutdown the underlying NIO EventLoopGroup:

    +
        try client.syncShutdown()
    +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class HTTPClient
    + +
    +
    + +
    +
    +
  • +
+
+
+ +
+
+
+ +
+
+ + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient.html b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient.html new file mode 100644 index 000000000..f10d04d59 --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient.html @@ -0,0 +1,1221 @@ + + + + HTTPClient Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+

HTTPClient

+
+
+
public class HTTPClient
+ +
+
+

HTTPClient class provides API for request execution.

+ +

Example:

+
    let client = HTTPClient(eventLoopGroupProvider = .createNew)
+    client.get(url: "https://swift.org/", deadline: .now() + .seconds(1)).whenComplete { result in
+        switch result {
+        case .failure(let error):
+            // process error
+        case .success(let response):
+            if let response.status == .ok {
+                // handle response
+            } else {
+                // handle remote error
+            }
+        }
+    }
+
+ +

It is important to close the client instance, for example in a defer statement, after use to cleanly shutdown the underlying NIO EventLoopGroup:

+
    try client.syncShutdown()
+
+ +
+
+ +
+
+
+
    +
  • +
    + + + + eventLoopGroup + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let eventLoopGroup: EventLoopGroup
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTPClient with specified EventLoopGroup provider and configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(eventLoopGroupProvider: EventLoopGroupProvider, configuration: Configuration = Configuration())
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + eventLoopGroupProvider + + +
    +

    Specify how EventLoopGroup will be created.

    +
    +
    + + configuration + + +
    +

    Client configuration.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + syncShutdown() + +
    +
    +
    +
    +
    +
    +

    Shuts down the client and EventLoopGroup if it was created by the client.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func syncShutdown() throws
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + get(url:deadline:) + +
    +
    +
    +
    +
    +
    +

    Execute GET request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func get(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute POST request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PATCH request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PUT request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + delete(url:deadline:) + +
    +
    +
    +
    +
    +
    +

    Execute DELETE request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func delete(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    The time when the request must have been completed by.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, eventLoop: EventLoopPreference, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          deadline: NIODeadline? = nil) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          eventLoop: EventLoopPreference,
    +                                                          deadline: NIODeadline? = nil) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + Configuration + +
    +
    +
    +
    +
    +
    +

    HTTPClient configuration.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Configuration
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum EventLoopGroupProvider
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + EventLoopPreference + +
    +
    +
    +
    +
    +
    +

    Specifies how the library will treat event loop passed by the user.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
+
+
+
    +
  • +
    + + + + Cookie + +
    +
    +
    +
    +
    +
    +

    A representation of an HTTP cookie.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Cookie
    + +
    +
    + +
    +
    +
  • +
+
+
+
    +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Represent HTTP response.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Response
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Body + +
    +
    +
    +
    +
    +
    +

    Represent request body.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Body
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Request + +
    +
    +
    +
    +
    +
    +

    Represent HTTP request.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Request
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Authorization + +
    +
    +
    +
    +
    +
    +

    HTTP authentication

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Authorization
    + +
    +
    + +
    +
    +
  • +
+
+
+
    +
  • +
    + + + + Task + +
    +
    +
    +
    +
    +
    +

    Response execution context. Will be created by the library and could be used for obtaining +EventLoopFuture<Response> of the execution or cancellation of the execution.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class Task<Response>
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Authorization.html b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Authorization.html new file mode 100644 index 000000000..ed90d57fe --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Authorization.html @@ -0,0 +1,270 @@ + + + + Authorization Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+

Authorization

+
+
+
public struct Authorization
+ +
+
+

HTTP authentication

+ +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Body.html b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Body.html new file mode 100644 index 000000000..d34043d23 --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Body.html @@ -0,0 +1,451 @@ + + + + Body Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+

Body

+
+
+
public struct Body
+ +
+
+

Represent request body.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + StreamWriter + +
    +
    +
    +
    +
    +
    +

    Chunk provider.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct StreamWriter
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + length + +
    +
    +
    +
    +
    +
    +

    Body size. Request validation will be failed with HTTPClientErrors.contentLengthMissing if nil, +unless Trasfer-Encoding: chunked header is set.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var length: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + stream + +
    +
    +
    +
    +
    +
    +

    Body chunk provider.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var stream: (StreamWriter) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + byteBuffer(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using ByteBuffer.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func byteBuffer(_ buffer: ByteBuffer) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + buffer + + +
    +

    Body ByteBuffer representation.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + stream(length:_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using StreamWriter.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func stream(length: Int? = nil, _ stream: @escaping (StreamWriter) -> EventLoopFuture<Void>) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + length + + +
    +

    Body size. Request validation will be failed with HTTPClientErrors.contentLengthMissing if nil, + unless Trasfer-Encoding: chunked header is set.

    +
    +
    + + stream + + +
    +

    Body chunk provider.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + data(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using Data.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func data(_ data: Data) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    Body Data representation.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + string(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using String.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func string(_ string: String) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + string + + +
    +

    Body String representation.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html new file mode 100644 index 000000000..560cf287e --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html @@ -0,0 +1,199 @@ + + + + StreamWriter Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+

StreamWriter

+
+
+
public struct StreamWriter
+ +
+
+

Chunk provider.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + write(_:) + +
    +
    +
    +
    +
    +
    +

    Write data to server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func write(_ data: IOData) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    IOData to write.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Configuration.html b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Configuration.html new file mode 100644 index 000000000..17bebc576 --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Configuration.html @@ -0,0 +1,511 @@ + + + + Configuration Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+

Configuration

+
+
+
public struct Configuration
+ +
+
+

HTTPClient configuration.

+ +
+
+ +
+
+
+ +
+
+
    +
  • +
    + + + + Timeout + +
    +
    +
    +
    +
    +
    +

    Timeout configuration

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Timeout
    + +
    +
    + +
    +
    +
  • +
+
+
+
    +
  • +
    + + + + Proxy + +
    +
    +
    +
    +
    +
    +

    Proxy server configuration +Specifies the remote address of an HTTP proxy.

    + +

    Adding an Proxy to your client’s HTTPClient.Configuration +will cause requests to be passed through the specified proxy using the +HTTP CONNECT method.

    + +

    If a TLSConfiguration is used in conjunction with HTTPClient.Configuration.Proxy, +TLS will be established after successful proxy, between your client +and the destination server.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    struct Proxy
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html new file mode 100644 index 000000000..d94a35d1e --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html @@ -0,0 +1,383 @@ + + + + Proxy Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+

Proxy

+
+
+
struct Proxy
+ +
+
+

Proxy server configuration +Specifies the remote address of an HTTP proxy.

+ +

Adding an Proxy to your client’s HTTPClient.Configuration +will cause requests to be passed through the specified proxy using the +HTTP CONNECT method.

+ +

If a TLSConfiguration is used in conjunction with HTTPClient.Configuration.Proxy, +TLS will be established after successful proxy, between your client +and the destination server.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server host.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + port + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server port.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var port: Int
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + authorization + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server authorization.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var authorization: HTTPClient.Authorization?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + server(host:port:) + +
    +
    +
    +
    +
    +
    +

    Create proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func server(host: String, port: Int) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + host + + +
    +

    proxy server host.

    +
    +
    + + port + + +
    +

    proxy server port.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func server(host: String, port: Int, authorization: HTTPClient.Authorization? = nil) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + host + + +
    +

    proxy server host.

    +
    +
    + + port + + +
    +

    proxy server port.

    +
    +
    + + authorization + + +
    +

    proxy server authorization.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html new file mode 100644 index 000000000..f5bf47447 --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html @@ -0,0 +1,271 @@ + + + + Timeout Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+

Timeout

+
+
+
public struct Timeout
+ +
+
+

Timeout configuration

+ +
+
+ +
+
+
+
    +
  • +
    + + + + connect + +
    +
    +
    +
    +
    +
    +

    Specifies connect timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var connect: TimeAmount?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + read + +
    +
    +
    +
    +
    +
    +

    Specifies read timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var read: TimeAmount?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + init(connect:read:) + +
    +
    +
    +
    +
    +
    +

    Create timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(connect: TimeAmount? = nil, read: TimeAmount? = nil)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + connect + + +
    +

    connect timeout.

    +
    +
    + + read + + +
    +

    read timeout.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Cookie.html b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Cookie.html new file mode 100644 index 000000000..a7c9693ad --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Cookie.html @@ -0,0 +1,588 @@ + + + + Cookie Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+

Cookie

+
+
+
public struct Cookie
+ +
+
+

A representation of an HTTP cookie.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + name + +
    +
    +
    +
    +
    +
    +

    The name of the cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var name: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + value + +
    +
    +
    +
    +
    +
    +

    The cookie’s string value.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var value: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + path + +
    +
    +
    +
    +
    +
    +

    The cookie’s path.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var path: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + domain + +
    +
    +
    +
    +
    +
    +

    The domain of the cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var domain: String?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + expires + +
    +
    +
    +
    +
    +
    +

    The cookie’s expiration date.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var expires: Date?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + maxAge + +
    +
    +
    +
    +
    +
    +

    The cookie’s age in seconds.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var maxAge: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + httpOnly + +
    +
    +
    +
    +
    +
    +

    Whether the cookie should only be sent to HTTP servers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var httpOnly: Bool
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + secure + +
    +
    +
    +
    +
    +
    +

    Whether the cookie should only be sent over secure channels.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var secure: Bool
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a Cookie by parsing a Set-Cookie header.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(header: String, defaultDomain: String)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + header + + +
    +

    String representation of the Set-Cookie response header.

    +
    +
    + + defaultDomain + + +
    +

    Default domain to use if cookie was sent without one.

    +
    +
    +
    +
    +

    Return Value

    +

    nil if the header is invalid.

    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(name: String, value: String, path: String = "/", domain: String? = nil, expires: Date? = nil, maxAge: Int? = nil, httpOnly: Bool = false, secure: Bool = false)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + name + + +
    +

    The name of the cookie.

    +
    +
    + + value + + +
    +

    The cookie’s string value.

    +
    +
    + + path + + +
    +

    The cookie’s path.

    +
    +
    + + domain + + +
    +

    The domain of the cookie, defaults to nil.

    +
    +
    + + expires + + +
    +

    The cookie’s expiration date, defaults to nil.

    +
    +
    + + maxAge + + +
    +

    The cookie’s age in seconds, defaults to nil.

    +
    +
    + + httpOnly + + +
    +

    Whether this cookie should be used by HTTP servers only, defaults to false.

    +
    +
    + + secure + + +
    +

    Whether this cookie should only be sent using secure channels, defaults to false.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html new file mode 100644 index 000000000..c9eccf980 --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html @@ -0,0 +1,210 @@ + + + + EventLoopGroupProvider Enumeration Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+

EventLoopGroupProvider

+
+
+
public enum EventLoopGroupProvider
+ +
+
+

Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + shared(_:) + +
    +
    +
    +
    +
    +
    +

    EventLoopGroup will be provided by the user. Owner of this group is responsible for its lifecycle.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case shared(EventLoopGroup)
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + createNew + +
    +
    +
    +
    +
    +
    +

    EventLoopGroup will be created by the client. When syncShutdown is called, created EventLoopGroup will be shut down as well.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case createNew
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html new file mode 100644 index 000000000..8e676b992 --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html @@ -0,0 +1,278 @@ + + + + EventLoopPreference Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+

EventLoopPreference

+
+
+
public struct EventLoopPreference
+ +
+
+

Specifies how the library will treat event loop passed by the user.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + indifferent + +
    +
    +
    +
    +
    +
    +

    Event Loop will be selected by the library.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let indifferent: HTTPClient.EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + prefers(_:) + +
    +
    +
    +
    +
    +
    +

    Library will try to use provided event loop if possible.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    @available(*, deprecated, renamed: "delegate(on:﹚")
    +public static func prefers(_ eventLoop: EventLoop) -> EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + delegate(on:) + +
    +
    +
    +
    +
    +
    +

    The delegate will be run on the specified EventLoop (and the Channel if possible).

    + +

    This will call the configured delegate on eventLoop and will try to use a Channel on the same +EventLoop but will not establish a new network connection just to satisfy the EventLoop preference if +another existing connection on a different EventLoop is readily available from a connection pool.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func delegate(on eventLoop: EventLoop) -> EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The delegate and the Channel will be run on the specified EventLoop.

    + +

    Use this for use-cases where you prefer a new connection to be established over re-using an existing +connection that might be on a different EventLoop.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func delegateAndChannel(on eventLoop: EventLoop) -> EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Request.html b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Request.html new file mode 100644 index 000000000..320cab881 --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Request.html @@ -0,0 +1,603 @@ + + + + Request Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+

Request

+
+
+
public struct Request
+ +
+
+

Represent HTTP request.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + method + +
    +
    +
    +
    +
    +
    +

    Request HTTP method, defaults to GET.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let method: HTTPMethod
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + url + +
    +
    +
    +
    +
    +
    +

    Remote URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let url: URL
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + scheme + +
    +
    +
    +
    +
    +
    +

    Remote HTTP scheme, resolved from URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let scheme: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Remote host, resolved from URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + headers + +
    +
    +
    +
    +
    +
    +

    Request custom HTTP Headers, defaults to no headers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var headers: HTTPHeaders
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + body + +
    +
    +
    +
    +
    +
    +

    Request body, defaults to no body.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var body: HTTPClient.Body?
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP request.

    +
    +

    Throws

    +
      +
    • invalidURL if URL cannot be parsed.
    • +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: String, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + version + + +
    +

    HTTP version.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTP Request.

    +
    +

    Throws

    +
      +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + version + + +
    +

    HTTP version.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + useTLS + +
    +
    +
    +
    +
    +
    +

    Whether request will be executed using secure socket.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var useTLS: Bool { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + port + +
    +
    +
    +
    +
    +
    +

    Resolved port.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var port: Int { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Response.html b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Response.html new file mode 100644 index 000000000..5d348a636 --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Response.html @@ -0,0 +1,389 @@ + + + + Response Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+

Response

+
+
+
public struct Response
+ +
+
+

Represent HTTP response.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Remote host of the request.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + status + +
    +
    +
    +
    +
    +
    +

    Response HTTP status.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var status: HTTPResponseStatus
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + headers + +
    +
    +
    +
    +
    +
    +

    Reponse HTTP headers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var headers: HTTPHeaders
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + body + +
    +
    +
    +
    +
    +
    +

    Response body.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var body: ByteBuffer?
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP Response.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(host: String, status: HTTPResponseStatus, headers: HTTPHeaders, body: ByteBuffer?)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + host + + +
    +

    Remote host of the request.

    +
    +
    + + status + + +
    +

    Response HTTP status.

    +
    +
    + + headers + + +
    +

    Reponse HTTP headers.

    +
    +
    + + body + + +
    +

    Response body.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
    +
  • +
    + + + + cookies + +
    +
    +
    +
    +
    +
    +

    List of HTTP cookies returned by the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var cookies: [HTTPClient.Cookie] { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Task.html b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Task.html new file mode 100644 index 000000000..f2650fd3d --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClient/Task.html @@ -0,0 +1,310 @@ + + + + Task Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+

Task

+
+
+
public final class Task<Response>
+ +
+
+

Response execution context. Will be created by the library and could be used for obtaining +EventLoopFuture<Response> of the execution or cancellation of the execution.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + currentEventLoop + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var currentEventLoop: EventLoop { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + eventLoop + +
    +
    +
    +
    +
    +
    +

    The EventLoop the delegate will be executed on.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let eventLoop: EventLoop
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + futureResult + +
    +
    +
    +
    +
    +
    +

    EventLoopFuture for the response returned by this request.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var futureResult: EventLoopFuture<Response> { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + wait() + +
    +
    +
    +
    +
    +
    +

    Waits for execution of this request to complete.

    +
    +

    Throws

    + The error value of the EventLoopFuture if it errors. + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func wait() throws -> Response
    + +
    +
    +
    +

    Return Value

    +

    The value of the EventLoopFuture when it completes.

    +
    + +
    +
    +
  • +
  • +
    + + + + cancel() + +
    +
    +
    +
    +
    +
    +

    Cancels the request execution.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func cancel()
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html new file mode 100644 index 000000000..96fd0cef2 --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html @@ -0,0 +1,238 @@ + + + + HTTPClientCopyingDelegate Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+

HTTPClientCopyingDelegate

+
+
+
public final class HTTPClientCopyingDelegate : HTTPClientResponseDelegate
+ +
+
+

Undocumented

+ +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/Extensions.html b/docs/1.0.0-alpha.4/AsyncHTTPClient/Extensions.html new file mode 100644 index 000000000..37780ce44 --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/Extensions.html @@ -0,0 +1,182 @@ + + + + Extensions Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ + +
+ + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/Extensions/TaskHandler.html b/docs/1.0.0-alpha.4/AsyncHTTPClient/Extensions/TaskHandler.html new file mode 100644 index 000000000..a09bd5cab --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/Extensions/TaskHandler.html @@ -0,0 +1,179 @@ + + + + TaskHandler Extension Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+

TaskHandler

+
+
+
extension TaskHandler: ChannelDuplexHandler
+ +
+
+

Undocumented

+ +
+
+ +
+
+
+
    +
  • +
    + + + + read(context:) + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func read(context: ChannelHandlerContext)
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/Protocols.html b/docs/1.0.0-alpha.4/AsyncHTTPClient/Protocols.html new file mode 100644 index 000000000..83902cbe3 --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/Protocols.html @@ -0,0 +1,187 @@ + + + + Protocols Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+

Protocols

+

The following protocols are available globally.

+ +
+
+ +
+
+
+
    +
  • + +
    +
    +
    +
    +
    +

    HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed. +You can implement this protocol if you need fine-grained control over an HTTP request/response, for example, if you want to inspect the response +headers before deciding whether to accept a response body, or if you want to stream your request body. Pass an instance of your conforming +class to the HTTPClient.execute() method and this package will call each delegate method appropriately as the request takes place.

    +
    +

    Note

    + This delegate is strongly held by the HTTPTaskHandler + for the duration of the Request processing and will be + released together with the HTTPTaskHandler when channel is closed. + Users of the library are not required to keep a reference to the + object that implements this protocol, but may do so if needed. + +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public protocol HTTPClientResponseDelegate : AnyObject
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html b/docs/1.0.0-alpha.4/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html new file mode 100644 index 000000000..3e0e450a2 --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html @@ -0,0 +1,653 @@ + + + + HTTPClientResponseDelegate Protocol Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+

HTTPClientResponseDelegate

+
+
+
public protocol HTTPClientResponseDelegate : AnyObject
+ +
+
+

HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed. +You can implement this protocol if you need fine-grained control over an HTTP request/response, for example, if you want to inspect the response +headers before deciding whether to accept a response body, or if you want to stream your request body. Pass an instance of your conforming +class to the HTTPClient.execute() method and this package will call each delegate method appropriately as the request takes place.

+
+

Note

+ This delegate is strongly held by the HTTPTaskHandler + for the duration of the Request processing and will be + released together with the HTTPTaskHandler when channel is closed. + Users of the library are not required to keep a reference to the + object that implements this protocol, but may do so if needed. + +
+ +
+
+ +
+
+
+
    +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    associatedtype Response
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequestHead(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when the request head is sent. Will be called once.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequestHead(task: HTTPClient.Task<Response>, _ head: HTTPRequestHead)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + head + + +
    +

    Request head.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequestPart(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when a part of the request body is sent. Could be called zero or more times.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequestPart(task: HTTPClient.Task<Response>, _ part: IOData)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + part + + +
    +

    Request body Part.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequest(task:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when the request is fully sent. Will be called once.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequest(task: HTTPClient.Task<Response>)
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveHead(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when response head is received. Will be called once. +You must return an EventLoopFuture<Void> that you complete when you have finished processing the body part. +You can create an already succeeded future by calling task.eventLoop.makeSucceededFuture(()).

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveHead(task: HTTPClient.Task<Response>, _ head: HTTPResponseHead) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + head + + +
    +

    Received reposonse head.

    +
    +
    +
    +
    +

    Return Value

    +

    EventLoopFuture that will be used for backpressure.

    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveBodyPart(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when part of a response body is received. Could be called zero or more times. +You must return an EventLoopFuture<Void> that you complete when you have finished processing the body part. +You can create an already succeeded future by calling task.eventLoop.makeSucceededFuture(()).

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveBodyPart(task: HTTPClient.Task<Response>, _ buffer: ByteBuffer) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + buffer + + +
    +

    Received body Part.

    +
    +
    +
    +
    +

    Return Value

    +

    EventLoopFuture that will be used for backpressure.

    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveError(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when error was thrown during request execution. Will be called zero or one time only. Request processing will be stopped after that.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveError(task: HTTPClient.Task<Response>, _ error: Error)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + error + + +
    +

    Error that occured during response processing.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Called when the complete HTTP request is finished. You must return an instance of your Response associated type. Will be called once, except if an error occurred.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didFinishRequest(task: HTTPClient.Task<Response>) throws -> Response
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    +
    +
    +

    Return Value

    +

    Result of processing.

    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/Structs.html b/docs/1.0.0-alpha.4/AsyncHTTPClient/Structs.html new file mode 100644 index 000000000..7b42a87b8 --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/Structs.html @@ -0,0 +1,175 @@ + + + + Structures Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+

Structures

+

The following structures are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + HTTPClientError + +
    +
    +
    +
    +
    +
    +

    Possible client errors.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct HTTPClientError : Error, Equatable, CustomStringConvertible
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/Structs/HTTPClientError.html b/docs/1.0.0-alpha.4/AsyncHTTPClient/Structs/HTTPClientError.html new file mode 100644 index 000000000..9ab56bdf2 --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/Structs/HTTPClientError.html @@ -0,0 +1,569 @@ + + + + HTTPClientError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+

HTTPClientError

+
+
+
public struct HTTPClientError : Error, Equatable, CustomStringConvertible
+ +
+
+

Possible client errors.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var description: String { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + invalidURL + +
    +
    +
    +
    +
    +
    +

    URL provided is invalid.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let invalidURL: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + emptyHost + +
    +
    +
    +
    +
    +
    +

    URL does not contain host.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let emptyHost: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + alreadyShutdown + +
    +
    +
    +
    +
    +
    +

    Client is shutdown and cannot be used for new requests.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let alreadyShutdown: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + emptyScheme + +
    +
    +
    +
    +
    +
    +

    URL does not contain scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let emptyScheme: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + unsupportedScheme(_:) + +
    +
    +
    +
    +
    +
    +

    Provided URL scheme is not supported, supported schemes are: http and https

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func unsupportedScheme(_ scheme: String) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + readTimeout + +
    +
    +
    +
    +
    +
    +

    Request timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let readTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Remote connection was closed unexpectedly.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let remoteConnectionClosed: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + cancelled + +
    +
    +
    +
    +
    +
    +

    Request was cancelled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let cancelled: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Request contains invalid identity encoding.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let identityCodingIncorrectlyPresent: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Request contains multiple chunks definitions.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let chunkedSpecifiedMultipleTimes: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + invalidProxyResponse + +
    +
    +
    +
    +
    +
    +

    Proxy response was invalid.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let invalidProxyResponse: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + contentLengthMissing + +
    +
    +
    +
    +
    +
    +

    Request does not contain Content-Length header.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let contentLengthMissing: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Proxy Authentication Required

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let proxyAuthenticationRequired: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/badge.svg b/docs/1.0.0-alpha.4/AsyncHTTPClient/badge.svg new file mode 100644 index 000000000..b28dab66c --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/badge.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + documentation + + + documentation + + + 89% + + + 89% + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/css/highlight.css b/docs/1.0.0-alpha.4/AsyncHTTPClient/css/highlight.css new file mode 100644 index 000000000..d0db0e13b --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/css/highlight.css @@ -0,0 +1,200 @@ +/* Credit to https://gist.github.com/wataru420/2048287 */ +.highlight { + /* Comment */ + /* Error */ + /* Keyword */ + /* Operator */ + /* Comment.Multiline */ + /* Comment.Preproc */ + /* Comment.Single */ + /* Comment.Special */ + /* Generic.Deleted */ + /* Generic.Deleted.Specific */ + /* Generic.Emph */ + /* Generic.Error */ + /* Generic.Heading */ + /* Generic.Inserted */ + /* Generic.Inserted.Specific */ + /* Generic.Output */ + /* Generic.Prompt */ + /* Generic.Strong */ + /* Generic.Subheading */ + /* Generic.Traceback */ + /* Keyword.Constant */ + /* Keyword.Declaration */ + /* Keyword.Pseudo */ + /* Keyword.Reserved */ + /* Keyword.Type */ + /* Literal.Number */ + /* Literal.String */ + /* Name.Attribute */ + /* Name.Builtin */ + /* Name.Class */ + /* Name.Constant */ + /* Name.Entity */ + /* Name.Exception */ + /* Name.Function */ + /* Name.Namespace */ + /* Name.Tag */ + /* Name.Variable */ + /* Operator.Word */ + /* Text.Whitespace */ + /* Literal.Number.Float */ + /* Literal.Number.Hex */ + /* Literal.Number.Integer */ + /* Literal.Number.Oct */ + /* Literal.String.Backtick */ + /* Literal.String.Char */ + /* Literal.String.Doc */ + /* Literal.String.Double */ + /* Literal.String.Escape */ + /* Literal.String.Heredoc */ + /* Literal.String.Interpol */ + /* Literal.String.Other */ + /* Literal.String.Regex */ + /* Literal.String.Single */ + /* Literal.String.Symbol */ + /* Name.Builtin.Pseudo */ + /* Name.Variable.Class */ + /* Name.Variable.Global */ + /* Name.Variable.Instance */ + /* Literal.Number.Integer.Long */ } + .highlight .c { + color: #999988; + font-style: italic; } + .highlight .err { + color: #a61717; + background-color: #e3d2d2; } + .highlight .k { + color: #000000; + font-weight: bold; } + .highlight .o { + color: #000000; + font-weight: bold; } + .highlight .cm { + color: #999988; + font-style: italic; } + .highlight .cp { + color: #999999; + font-weight: bold; } + .highlight .c1 { + color: #999988; + font-style: italic; } + .highlight .cs { + color: #999999; + font-weight: bold; + font-style: italic; } + .highlight .gd { + color: #000000; + background-color: #ffdddd; } + .highlight .gd .x { + color: #000000; + background-color: #ffaaaa; } + .highlight .ge { + color: #000000; + font-style: italic; } + .highlight .gr { + color: #aa0000; } + .highlight .gh { + color: #999999; } + .highlight .gi { + color: #000000; + background-color: #ddffdd; } + .highlight .gi .x { + color: #000000; + background-color: #aaffaa; } + .highlight .go { + color: #888888; } + .highlight .gp { + color: #555555; } + .highlight .gs { + font-weight: bold; } + .highlight .gu { + color: #aaaaaa; } + .highlight .gt { + color: #aa0000; } + .highlight .kc { + color: #000000; + font-weight: bold; } + .highlight .kd { + color: #000000; + font-weight: bold; } + .highlight .kp { + color: #000000; + font-weight: bold; } + .highlight .kr { + color: #000000; + font-weight: bold; } + .highlight .kt { + color: #445588; } + .highlight .m { + color: #009999; } + .highlight .s { + color: #d14; } + .highlight .na { + color: #008080; } + .highlight .nb { + color: #0086B3; } + .highlight .nc { + color: #445588; + font-weight: bold; } + .highlight .no { + color: #008080; } + .highlight .ni { + color: #800080; } + .highlight .ne { + color: #990000; + font-weight: bold; } + .highlight .nf { + color: #990000; } + .highlight .nn { + color: #555555; } + .highlight .nt { + color: #000080; } + .highlight .nv { + color: #008080; } + .highlight .ow { + color: #000000; + font-weight: bold; } + .highlight .w { + color: #bbbbbb; } + .highlight .mf { + color: #009999; } + .highlight .mh { + color: #009999; } + .highlight .mi { + color: #009999; } + .highlight .mo { + color: #009999; } + .highlight .sb { + color: #d14; } + .highlight .sc { + color: #d14; } + .highlight .sd { + color: #d14; } + .highlight .s2 { + color: #d14; } + .highlight .se { + color: #d14; } + .highlight .sh { + color: #d14; } + .highlight .si { + color: #d14; } + .highlight .sx { + color: #d14; } + .highlight .sr { + color: #009926; } + .highlight .s1 { + color: #d14; } + .highlight .ss { + color: #990073; } + .highlight .bp { + color: #999999; } + .highlight .vc { + color: #008080; } + .highlight .vg { + color: #008080; } + .highlight .vi { + color: #008080; } + .highlight .il { + color: #009999; } diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/css/jazzy.css b/docs/1.0.0-alpha.4/AsyncHTTPClient/css/jazzy.css new file mode 100644 index 000000000..24cfe4aec --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/css/jazzy.css @@ -0,0 +1,372 @@ +*, *:before, *:after { + box-sizing: inherit; } + +body { + margin: 0; + background: #fff; + color: #333; + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + letter-spacing: .2px; + -webkit-font-smoothing: antialiased; + box-sizing: border-box; } + +h1 { + font-size: 2rem; + font-weight: 700; + margin: 1.275em 0 0.6em; } + +h2 { + font-size: 1.75rem; + font-weight: 700; + margin: 1.275em 0 0.3em; } + +h3 { + font-size: 1.5rem; + font-weight: 700; + margin: 1em 0 0.3em; } + +h4 { + font-size: 1.25rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h5 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h6 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; + color: #777; } + +p { + margin: 0 0 1em; } + +ul, ol { + padding: 0 0 0 2em; + margin: 0 0 0.85em; } + +blockquote { + margin: 0 0 0.85em; + padding: 0 15px; + color: #858585; + border-left: 4px solid #e5e5e5; } + +img { + max-width: 100%; } + +a { + color: #4183c4; + text-decoration: none; } + a:hover, a:focus { + outline: 0; + text-decoration: underline; } + a.discouraged { + text-decoration: line-through; } + a.discouraged:hover, a.discouraged:focus { + text-decoration: underline line-through; } + +table { + background: #fff; + width: 100%; + border-collapse: collapse; + border-spacing: 0; + overflow: auto; + margin: 0 0 0.85em; } + +tr:nth-child(2n) { + background-color: #fbfbfb; } + +th, td { + padding: 6px 13px; + border: 1px solid #ddd; } + +pre { + margin: 0 0 1.275em; + padding: .85em 1em; + overflow: auto; + background: #f7f7f7; + font-size: .85em; + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +code { + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +p > code, li > code { + background: #f7f7f7; + padding: .2em; } + p > code:before, p > code:after, li > code:before, li > code:after { + letter-spacing: -.2em; + content: "\00a0"; } + +pre code { + padding: 0; + white-space: pre; } + +.content-wrapper { + display: flex; + flex-direction: column; } + @media (min-width: 768px) { + .content-wrapper { + flex-direction: row; } } + +.header { + display: flex; + padding: 8px; + font-size: 0.875em; + background: #444; + color: #999; } + +.header-col { + margin: 0; + padding: 0 8px; } + +.header-col--primary { + flex: 1; } + +.header-link { + color: #fff; } + +.header-icon { + padding-right: 6px; + vertical-align: -4px; + height: 16px; } + +.breadcrumbs { + font-size: 0.875em; + padding: 8px 16px; + margin: 0; + background: #fbfbfb; + border-bottom: 1px solid #ddd; } + +.carat { + height: 10px; + margin: 0 5px; } + +.navigation { + order: 2; } + @media (min-width: 768px) { + .navigation { + order: 1; + width: 25%; + max-width: 300px; + padding-bottom: 64px; + overflow: hidden; + word-wrap: normal; + background: #fbfbfb; + border-right: 1px solid #ddd; } } + +.nav-groups { + list-style-type: none; + padding-left: 0; } + +.nav-group-name { + border-bottom: 1px solid #ddd; + padding: 8px 0 8px 16px; } + +.nav-group-name-link { + color: #333; } + +.nav-group-tasks { + margin: 8px 0; + padding: 0 0 0 8px; } + +.nav-group-task { + font-size: 1em; + list-style-type: none; + white-space: nowrap; } + +.nav-group-task-link { + color: #808080; } + +.main-content { + order: 1; } + @media (min-width: 768px) { + .main-content { + order: 2; + flex: 1; + padding-bottom: 60px; } } + +.section { + padding: 0 32px; + border-bottom: 1px solid #ddd; } + +.section-content { + max-width: 834px; + margin: 0 auto; + padding: 16px 0; } + +.section-name { + color: #666; + display: block; } + +.declaration .highlight { + overflow-x: initial; + padding: 8px 0; + margin: 0; + background-color: transparent; + border: none; } + +.task-group-section { + border-top: 1px solid #ddd; } + +.task-group { + padding-top: 0px; } + +.task-name-container a[name]:before { + content: ""; + display: block; } + +.item-container { + padding: 0; } + +.item { + padding-top: 8px; + width: 100%; + list-style-type: none; } + .item a[name]:before { + content: ""; + display: block; } + .item .token, .item .direct-link { + padding-left: 3px; + margin-left: 0px; + font-size: 1rem; } + .item .declaration-note { + font-size: .85em; + color: #808080; + font-style: italic; } + +.pointer-container { + border-bottom: 1px solid #ddd; + left: -23px; + padding-bottom: 13px; + position: relative; + width: 110%; } + +.pointer { + left: 21px; + top: 7px; + display: block; + position: absolute; + width: 12px; + height: 12px; + border-left: 1px solid #ddd; + border-top: 1px solid #ddd; + background: #fff; + transform: rotate(45deg); } + +.height-container { + display: none; + position: relative; + width: 100%; + overflow: hidden; } + .height-container .section { + background: #fff; + border: 1px solid #ddd; + border-top-width: 0; + padding-top: 10px; + padding-bottom: 5px; + padding: 8px 16px; } + +.aside, .language { + padding: 6px 12px; + margin: 12px 0; + border-left: 5px solid #dddddd; + overflow-y: hidden; } + .aside .aside-title, .language .aside-title { + font-size: 9px; + letter-spacing: 2px; + text-transform: uppercase; + padding-bottom: 0; + margin: 0; + color: #aaa; + -webkit-user-select: none; } + .aside p:last-child, .language p:last-child { + margin-bottom: 0; } + +.language { + border-left: 5px solid #cde9f4; } + .language .aside-title { + color: #4183c4; } + +.aside-warning, .aside-deprecated, .aside-unavailable { + border-left: 5px solid #ff6666; } + .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { + color: #ff0000; } + +.graybox { + border-collapse: collapse; + width: 100%; } + .graybox p { + margin: 0; + word-break: break-word; + min-width: 50px; } + .graybox td { + border: 1px solid #ddd; + padding: 5px 25px 5px 10px; + vertical-align: middle; } + .graybox tr td:first-of-type { + text-align: right; + padding: 7px; + vertical-align: top; + word-break: normal; + width: 40px; } + +.slightly-smaller { + font-size: 0.9em; } + +.footer { + padding: 8px 16px; + background: #444; + color: #ddd; + font-size: 0.8em; } + .footer p { + margin: 8px 0; } + .footer a { + color: #fff; } + +html.dash .header, html.dash .breadcrumbs, html.dash .navigation { + display: none; } +html.dash .height-container { + display: block; } + +form[role=search] input { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 24px; + padding: 0 10px; + margin: 0; + border: none; + border-radius: 1em; } + .loading form[role=search] input { + background: white url(http://github.com/img/spinner.gif) center right 4px no-repeat; } +form[role=search] .tt-menu { + margin: 0; + min-width: 300px; + background: #fbfbfb; + color: #333; + border: 1px solid #ddd; } +form[role=search] .tt-highlight { + font-weight: bold; } +form[role=search] .tt-suggestion { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 0 8px; } + form[role=search] .tt-suggestion span { + display: table-cell; + white-space: nowrap; } + form[role=search] .tt-suggestion .doc-parent-name { + width: 100%; + text-align: right; + font-weight: normal; + font-size: 0.9em; + padding-left: 16px; } +form[role=search] .tt-suggestion:hover, +form[role=search] .tt-suggestion.tt-cursor { + cursor: pointer; + background-color: #4183c4; + color: #fff; } +form[role=search] .tt-suggestion:hover .doc-parent-name, +form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { + color: #fff; } diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/img/carat.png b/docs/1.0.0-alpha.4/AsyncHTTPClient/img/carat.png new file mode 100755 index 000000000..29d2f7fd4 Binary files /dev/null and b/docs/1.0.0-alpha.4/AsyncHTTPClient/img/carat.png differ diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/img/dash.png b/docs/1.0.0-alpha.4/AsyncHTTPClient/img/dash.png new file mode 100755 index 000000000..6f694c7a0 Binary files /dev/null and b/docs/1.0.0-alpha.4/AsyncHTTPClient/img/dash.png differ diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/img/gh.png b/docs/1.0.0-alpha.4/AsyncHTTPClient/img/gh.png new file mode 100755 index 000000000..628da97c7 Binary files /dev/null and b/docs/1.0.0-alpha.4/AsyncHTTPClient/img/gh.png differ diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/img/spinner.gif b/docs/1.0.0-alpha.4/AsyncHTTPClient/img/spinner.gif new file mode 100644 index 000000000..e3038d0a4 Binary files /dev/null and b/docs/1.0.0-alpha.4/AsyncHTTPClient/img/spinner.gif differ diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/index.html b/docs/1.0.0-alpha.4/AsyncHTTPClient/index.html new file mode 100644 index 000000000..551d90d9d --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/index.html @@ -0,0 +1,141 @@ + + + + AsyncHTTPClient Reference + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+ +

AsyncHTTPClient Docs

+ +

AsyncHTTPClient is a Swift HTTP Client package.

+ +

To get started with AsyncHTTPClient, import AsyncHTTPClient. The +most important type is HTTPClient +which you can use to emit log messages.

+ +
+
+ + +
+
+ + + + diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/js/jazzy.js b/docs/1.0.0-alpha.4/AsyncHTTPClient/js/jazzy.js new file mode 100755 index 000000000..c31dc05e4 --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/js/jazzy.js @@ -0,0 +1,59 @@ +window.jazzy = {'docset': false} +if (typeof window.dash != 'undefined') { + document.documentElement.className += ' dash' + window.jazzy.docset = true +} +if (navigator.userAgent.match(/xcode/i)) { + document.documentElement.className += ' xcode' + window.jazzy.docset = true +} + +function toggleItem($link, $content) { + var animationDuration = 300; + $link.toggleClass('token-open'); + $content.slideToggle(animationDuration); +} + +function itemLinkToContent($link) { + return $link.parent().parent().next(); +} + +// On doc load + hash-change, open any targetted item +function openCurrentItemIfClosed() { + if (window.jazzy.docset) { + return; + } + var $link = $(`.token[href="http://github.com/$%7Blocation.hash%7D"]`); + $content = itemLinkToContent($link); + if ($content.is(':hidden')) { + toggleItem($link, $content); + } +} + +$(openCurrentItemIfClosed); +$(window).on('hashchange', openCurrentItemIfClosed); + +// On item link ('token') click, toggle its discussion +$('.token').on('click', function(event) { + if (window.jazzy.docset) { + return; + } + var $link = $(this); + toggleItem($link, itemLinkToContent($link)); + + // Keeps the document from jumping to the hash. + var href = $link.attr('href'); + if (history.pushState) { + history.pushState({}, '', href); + } else { + location.hash = href; + } + event.preventDefault(); +}); + +// Clicks on links to the current, closed, item need to open the item +$("a:not('.token')").on('click', function() { + if (location == this.href) { + openCurrentItemIfClosed(); + } +}); diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/js/jazzy.search.js b/docs/1.0.0-alpha.4/AsyncHTTPClient/js/jazzy.search.js new file mode 100644 index 000000000..e3d1ab905 --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/js/jazzy.search.js @@ -0,0 +1,70 @@ +$(function(){ + var $typeahead = $('[data-typeahead]'); + var $form = $typeahead.parents('form'); + var searchURL = $form.attr('action'); + + function displayTemplate(result) { + return result.name; + } + + function suggestionTemplate(result) { + var t = '
'; + t += '' + result.name + ''; + if (result.parent_name) { + t += '' + result.parent_name + ''; + } + t += '
'; + return t; + } + + $typeahead.one('focus', function() { + $form.addClass('loading'); + + $.getJSON(searchURL).then(function(searchData) { + const searchIndex = lunr(function() { + this.ref('url'); + this.field('name'); + this.field('abstract'); + for (const [url, doc] of Object.entries(searchData)) { + this.add({url: url, name: doc.name, abstract: doc.abstract}); + } + }); + + $typeahead.typeahead( + { + highlight: true, + minLength: 3, + autoselect: true + }, + { + limit: 10, + display: displayTemplate, + templates: { suggestion: suggestionTemplate }, + source: function(query, sync) { + const lcSearch = query.toLowerCase(); + const results = searchIndex.query(function(q) { + q.term(lcSearch, { boost: 100 }); + q.term(lcSearch, { + boost: 10, + wildcard: lunr.Query.wildcard.TRAILING + }); + }).map(function(result) { + var doc = searchData[result.ref]; + doc.url = result.ref; + return doc; + }); + sync(results); + } + } + ); + $form.removeClass('loading'); + $typeahead.trigger('focus'); + }); + }); + + var baseURL = searchURL.slice(0, -"search.json".length); + + $typeahead.on('typeahead:select', function(e, result) { + window.location = baseURL + result.url; + }); +}); diff --git a/docs/1.0.0-alpha.4/AsyncHTTPClient/js/jquery.min.js b/docs/1.0.0-alpha.4/AsyncHTTPClient/js/jquery.min.js new file mode 100755 index 000000000..4d9b3a258 --- /dev/null +++ b/docs/1.0.0-alpha.4/AsyncHTTPClient/js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function e(t){return"function"==typeof t&&"number"!=typeof t.nodeType},y=function e(t){return null!=t&&t===t.window},v={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement("script");if(o.text=e,n)for(i in v)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[c.call(e)]||"object":typeof e}var b="3.3.1",w=function(e,t){return new w.fn.init(e,t)},T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;w.fn=w.prototype={jquery:"3.3.1",constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b="sizzle"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function oe(e,t,r,i){var o,s,l,c,f,h,v,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=J.exec(e)))if(o=f[1]){if(9===T){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!S[e+" "]&&(!y||!y.test(e))){if(1!==T)m=t,v=e;else if("object"!==t.nodeName.toLowerCase()){(c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;while(s--)h[s]="#"+c+" "+ve(h[s]);v=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(v)try{return L.apply(r,m.querySelectorAll(v)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,i)}function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}return t}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ie(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return se(function(t){return t=+t,se(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(d=a,h=d.documentElement,g=!o(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=Q.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&g)return t.getElementsByClassName(e)},v=[],y=[],(n.qsa=Q.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||y.push("~="),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||y.push(".#.+[+~]")}),ue(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(n.matchesSelector=Q.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),v.push("!=",W)}),y=y.length&&new RegExp(y.join("|")),v=v.length&&new RegExp(v.join("|")),t=Q.test(h.compareDocumentPosition),x=t||Q.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===d?-1:t===d?1:i?-1:o?1:c?O(c,e)-O(c,t):0;if(i===o)return ce(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[t+" "]&&(!v||!v.test(t))&&(!y||!y.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,d,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&N.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.escape=function(e){return(e+"").replace(te,ne)},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),f){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else while(t=e[r++])n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:se,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace($," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",y=t.parentNode,v=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(y){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===v:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?y.firstChild:y.lastChild],a&&m){x=(d=(l=(c=(f=(p=y)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&y.childNodes[d];while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===v:1===p.nodeType)&&++x&&(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p===t))break;return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){var r,o=i(e,t),a=o.length;while(a--)e[r=O(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return oe(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:se(function(e){return U.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function be(e,t,n){for(var r=0,i=t.length;r-1&&(o[l]=!(a[l]=f))}}else v=we(v===a?v.splice(h,v.length):v),i?i(null,a,v,u):L.apply(a,v)})}function Ce(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u1&&xe(p),u>1&&ve(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u0,i=e.length>0,o=function(o,a,s,u,c){var f,h,y,v=0,m="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(i&&f){h=0,a||f.ownerDocument===d||(p(f),s=!g);while(y=e[h++])if(y(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!y&&f)&&v--,o&&x.push(f))}if(v+=m,n&&m!==v){h=0;while(y=t[h++])y(x,b,a,s);if(o){if(v>0)while(m--)x[m]||b[m]||(b[m]=j.call(u));b=we(b)}L.apply(u,b),c&&!o&&b.length>0&&v+t.length>1&&oe.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(o):o}return s=oe.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=a(e)),n=t.length;while(n--)(o=Ce(t[n]))[b]?r.push(o):i.push(o);(o=S(e,Ee(i,r))).selector=e}return o},u=oe.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}o=V.needsContext.test(e)?0:u.length;while(o--){if(l=u[o],r.relative[c=l.type])break;if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&ve(u)))return L.apply(n,i),n;break}}}return(p||s(e,d))(i,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(e);w.find=E,w.expr=E.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=E.uniqueSort,w.text=E.getText,w.isXMLDoc=E.isXML,w.contains=E.contains,w.escapeSelector=E.escape;var k=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=w.expr.match.needsContext;function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var A=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return u.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&D.test(e)?w(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:L.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),A.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,q=w(r);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&w.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?w.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?u.call(w(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,n){return k(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,n){return k(e,"nextSibling",n)},prevUntil:function(e,t,n){return k(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return N(e,"iframe")?e.contentDocument:(N(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(O[e]||w.uniqueSort(i),H.test(e)&&i.reverse()),this.pushStack(i)}});var M=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!0}),t}w.Callbacks=function(e){e="string"==typeof e?R(e):w.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1){n=a.shift();while(++s-1)o.splice(n,1),n<=s&&s--}),this},has:function(e){return e?w.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l};function I(e){return e}function W(e){throw e}function $(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.extend({Deferred:function(t){var n=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(t){w.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t=o&&(r!==W&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),e.setTimeout(c))}}return w.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:I,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:I)),n[2][3].add(a(0,e,g(r)?r:W))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=w.Deferred(),s=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&($(e,a.done(s(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();while(n--)$(i[n],s(n),a.reject);return a.promise()}});var B=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&B.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},w.readyException=function(t){e.setTimeout(function(){throw t})};var F=w.Deferred();w.fn.ready=function(e){return F.then(e)["catch"](function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||F.resolveWith(r,[w]))}}),w.ready.then=F.then;function _(){r.removeEventListener("DOMContentLoaded",_),e.removeEventListener("load",_),w.ready()}"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(w.ready):(r.addEventListener("DOMContentLoaded",_),e.addEventListener("load",_));var z=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n)){i=!0;for(s in n)z(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,g(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each(function(){K.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=J.get(e,t),n&&(!r||Array.isArray(n)?r=J.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),o=w._queueHooks(e,t),a=function(){w.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return J.get(e,n)||J.access(e,n,{empty:w.Callbacks("once memory").add(function(){J.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&N(e,t)?w.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ye(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var be=r.documentElement,we=/^key/,Te=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function ke(){return!1}function Se(){try{return r.activeElement}catch(e){}}function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)De(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=ke;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.get(e);if(y){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(be,i),n.guid||(n.guid=w.guid++),(u=y.events)||(u=y.events={}),(a=y.handle)||(a=y.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(M)||[""]).length;while(l--)d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),w.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.hasData(e)&&J.get(e);if(y&&(u=y.events)){l=(t=(t||"").match(M)||[""]).length;while(l--)if(s=Ce.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){f=w.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,y.handle)||w.removeEvent(e,d,y.handle),delete u[d])}else for(d in u)w.event.remove(e,d+t[l],n,r,!0);w.isEmptyObject(u)&&J.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,s,u=new Array(arguments.length),l=(J.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(u[0]=t,n=1;n=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,Ae=/\s*$/g;function Le(e,t){return N(e,"table")&&N(11!==t.nodeType?t:t.firstChild,"tr")?w(e).children("tbody")[0]||e:e}function He(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Oe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Pe(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(J.hasData(e)&&(o=J.access(e),a=J.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n1&&"string"==typeof y&&!h.checkClone&&je.test(y))return e.each(function(i){var o=e.eq(i);v&&(t[0]=y.call(this,i,o.html())),Re(o,t,n,r)});if(p&&(i=xe(t,e[0].ownerDocument,!1,e,r),o=i.firstChild,1===i.childNodes.length&&(i=o),o||r)){for(u=(s=w.map(ye(i,"script"),He)).length;f")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=w.contains(e.ownerDocument,e);if(!(h.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||w.isXMLDoc(e)))for(a=ye(s),r=0,i=(o=ye(e)).length;r0&&ve(a,!u&&ye(e,"script")),s},cleanData:function(e){for(var t,n,r,i=w.event.special,o=0;void 0!==(n=e[o]);o++)if(Y(n)){if(t=n[J.expando]){if(t.events)for(r in t.events)i[r]?w.event.remove(n,r):w.removeEvent(n,r,t.handle);n[J.expando]=void 0}n[K.expando]&&(n[K.expando]=void 0)}}}),w.fn.extend({detach:function(e){return Ie(this,e,!0)},remove:function(e){return Ie(this,e)},text:function(e){return z(this,function(e){return void 0===e?w.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Re(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Le(this,e).appendChild(e)})},prepend:function(){return Re(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Le(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(w.cleanData(ye(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return w.clone(this,e,t)})},html:function(e){return z(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ae.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=w.htmlPrefilter(e);try{for(;n=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))),u}function et(e,t,n){var r=$e(e),i=Fe(e,t,r),o="border-box"===w.css(e,"boxSizing",!1,r),a=o;if(We.test(i)){if(!n)return i;i="auto"}return a=a&&(h.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===w.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+Ze(e,t,n||(o?"border":"content"),a,r,i)+"px"}w.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Fe(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=G(t),u=Xe.test(t),l=e.style;if(u||(t=Je(s)),a=w.cssHooks[t]||w.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"==(o=typeof n)&&(i=ie.exec(n))&&i[1]&&(n=ue(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(w.cssNumber[s]?"":"px")),h.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=G(t);return Xe.test(t)||(t=Je(s)),(a=w.cssHooks[t]||w.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Fe(e,t,r)),"normal"===i&&t in Ve&&(i=Ve[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),w.each(["height","width"],function(e,t){w.cssHooks[t]={get:function(e,n,r){if(n)return!ze.test(w.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):se(e,Ue,function(){return et(e,t,r)})},set:function(e,n,r){var i,o=$e(e),a="border-box"===w.css(e,"boxSizing",!1,o),s=r&&Ze(e,t,r,a,o);return a&&h.scrollboxSize()===o.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-Ze(e,t,"border",!1,o)-.5)),s&&(i=ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=w.css(e,t)),Ke(e,n,s)}}}),w.cssHooks.marginLeft=_e(h.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Fe(e,"marginLeft"))||e.getBoundingClientRect().left-se(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),w.each({margin:"",padding:"",border:"Width"},function(e,t){w.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+oe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(w.cssHooks[e+t].set=Ke)}),w.fn.extend({css:function(e,t){return z(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=$e(e),i=t.length;a1)}});function tt(e,t,n,r,i){return new tt.prototype.init(e,t,n,r,i)}w.Tween=tt,tt.prototype={constructor:tt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||w.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(w.cssNumber[n]?"":"px")},cur:function(){var e=tt.propHooks[this.prop];return e&&e.get?e.get(this):tt.propHooks._default.get(this)},run:function(e){var t,n=tt.propHooks[this.prop];return this.options.duration?this.pos=t=w.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):tt.propHooks._default.set(this),this}},tt.prototype.init.prototype=tt.prototype,tt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=w.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){w.fx.step[e.prop]?w.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[w.cssProps[e.prop]]&&!w.cssHooks[e.prop]?e.elem[e.prop]=e.now:w.style(e.elem,e.prop,e.now+e.unit)}}},tt.propHooks.scrollTop=tt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},w.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},w.fx=tt.prototype.init,w.fx.step={};var nt,rt,it=/^(?:toggle|show|hide)$/,ot=/queueHooks$/;function at(){rt&&(!1===r.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(at):e.setTimeout(at,w.fx.interval),w.fx.tick())}function st(){return e.setTimeout(function(){nt=void 0}),nt=Date.now()}function ut(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=oe[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function lt(e,t,n){for(var r,i=(pt.tweeners[t]||[]).concat(pt.tweeners["*"]),o=0,a=i.length;o1)},removeAttr:function(e){return this.each(function(){w.removeAttr(this,e)})}}),w.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?w.prop(e,t,n):(1===o&&w.isXMLDoc(e)||(i=w.attrHooks[t.toLowerCase()]||(w.expr.match.bool.test(t)?dt:void 0)),void 0!==n?null===n?void w.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=w.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!h.radioValue&&"radio"===t&&N(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(M);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),dt={set:function(e,t,n){return!1===t?w.removeAttr(e,n):e.setAttribute(n,n),n}},w.each(w.expr.match.bool.source.match(/\w+/g),function(e,t){var n=ht[t]||w.find.attr;ht[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=ht[a],ht[a]=i,i=null!=n(e,t,r)?a:null,ht[a]=o),i}});var gt=/^(?:input|select|textarea|button)$/i,yt=/^(?:a|area)$/i;w.fn.extend({prop:function(e,t){return z(this,w.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[w.propFix[e]||e]})}}),w.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&w.isXMLDoc(e)||(t=w.propFix[t]||t,i=w.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=w.find.attr(e,"tabindex");return t?parseInt(t,10):gt.test(e.nodeName)||yt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),h.optSelected||(w.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),w.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){w.propFix[this.toLowerCase()]=this});function vt(e){return(e.match(M)||[]).join(" ")}function mt(e){return e.getAttribute&&e.getAttribute("class")||""}function xt(e){return Array.isArray(e)?e:"string"==typeof e?e.match(M)||[]:[]}w.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).addClass(e.call(this,t,mt(this)))});if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).removeClass(e.call(this,t,mt(this)))});if(!arguments.length)return this.attr("class","");if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])while(r.indexOf(" "+o+" ")>-1)r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each(function(n){w(this).toggleClass(e.call(this,n,mt(this),t),t)}):this.each(function(){var t,i,o,a;if(r){i=0,o=w(this),a=xt(e);while(t=a[i++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else void 0!==e&&"boolean"!==n||((t=mt(this))&&J.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":J.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&(" "+vt(mt(n))+" ").indexOf(t)>-1)return!0;return!1}});var bt=/\r/g;w.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=g(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,w(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=w.map(i,function(e){return null==e?"":e+""})),(t=w.valHooks[this.type]||w.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=w.valHooks[i.type]||w.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(bt,""):null==n?"":n}}}),w.extend({valHooks:{option:{get:function(e){var t=w.find.attr(e,"value");return null!=t?t:vt(w.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),w.each(["radio","checkbox"],function(){w.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=w.inArray(w(e).val(),t)>-1}},h.checkOn||(w.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),h.focusin="onfocusin"in e;var wt=/^(?:focusinfocus|focusoutblur)$/,Tt=function(e){e.stopPropagation()};w.extend(w.event,{trigger:function(t,n,i,o){var a,s,u,l,c,p,d,h,v=[i||r],m=f.call(t,"type")?t.type:t,x=f.call(t,"namespace")?t.namespace.split("."):[];if(s=h=u=i=i||r,3!==i.nodeType&&8!==i.nodeType&&!wt.test(m+w.event.triggered)&&(m.indexOf(".")>-1&&(m=(x=m.split(".")).shift(),x.sort()),c=m.indexOf(":")<0&&"on"+m,t=t[w.expando]?t:new w.Event(m,"object"==typeof t&&t),t.isTrigger=o?2:3,t.namespace=x.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+x.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:w.makeArray(n,[t]),d=w.event.special[m]||{},o||!d.trigger||!1!==d.trigger.apply(i,n))){if(!o&&!d.noBubble&&!y(i)){for(l=d.delegateType||m,wt.test(l+m)||(s=s.parentNode);s;s=s.parentNode)v.push(s),u=s;u===(i.ownerDocument||r)&&v.push(u.defaultView||u.parentWindow||e)}a=0;while((s=v[a++])&&!t.isPropagationStopped())h=s,t.type=a>1?l:d.bindType||m,(p=(J.get(s,"events")||{})[t.type]&&J.get(s,"handle"))&&p.apply(s,n),(p=c&&s[c])&&p.apply&&Y(s)&&(t.result=p.apply(s,n),!1===t.result&&t.preventDefault());return t.type=m,o||t.isDefaultPrevented()||d._default&&!1!==d._default.apply(v.pop(),n)||!Y(i)||c&&g(i[m])&&!y(i)&&((u=i[c])&&(i[c]=null),w.event.triggered=m,t.isPropagationStopped()&&h.addEventListener(m,Tt),i[m](),t.isPropagationStopped()&&h.removeEventListener(m,Tt),w.event.triggered=void 0,u&&(i[c]=u)),t.result}},simulate:function(e,t,n){var r=w.extend(new w.Event,n,{type:e,isSimulated:!0});w.event.trigger(r,null,t)}}),w.fn.extend({trigger:function(e,t){return this.each(function(){w.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return w.event.trigger(e,t,n,!0)}}),h.focusin||w.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){w.event.simulate(t,e.target,w.event.fix(e))};w.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=J.access(r,t);i||r.addEventListener(e,n,!0),J.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=J.access(r,t)-1;i?J.access(r,t,i):(r.removeEventListener(e,n,!0),J.remove(r,t))}}});var Ct=e.location,Et=Date.now(),kt=/\?/;w.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||w.error("Invalid XML: "+t),n};var St=/\[\]$/,Dt=/\r?\n/g,Nt=/^(?:submit|button|image|reset|file)$/i,At=/^(?:input|select|textarea|keygen)/i;function jt(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n||St.test(e)?r(e,i):jt(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==x(t))r(e,t);else for(i in t)jt(e+"["+i+"]",t[i],n,r)}w.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!w.isPlainObject(e))w.each(e,function(){i(this.name,this.value)});else for(n in e)jt(n,e[n],t,i);return r.join("&")},w.fn.extend({serialize:function(){return w.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=w.prop(this,"elements");return e?w.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!w(this).is(":disabled")&&At.test(this.nodeName)&&!Nt.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=w(this).val();return null==n?null:Array.isArray(n)?w.map(n,function(e){return{name:t.name,value:e.replace(Dt,"\r\n")}}):{name:t.name,value:n.replace(Dt,"\r\n")}}).get()}});var qt=/%20/g,Lt=/#.*$/,Ht=/([?&])_=[^&]*/,Ot=/^(.*?):[ \t]*([^\r\n]*)$/gm,Pt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Mt=/^(?:GET|HEAD)$/,Rt=/^\/\//,It={},Wt={},$t="*/".concat("*"),Bt=r.createElement("a");Bt.href=Ct.href;function Ft(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(M)||[];if(g(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function _t(e,t,n,r){var i={},o=e===Wt;function a(s){var u;return i[s]=!0,w.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)}),u}return a(t.dataTypes[0])||!i["*"]&&a("*")}function zt(e,t){var n,r,i=w.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&w.extend(!0,e,r),e}function Xt(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function Ut(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}w.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:Pt.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":$t,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":w.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?zt(zt(e,w.ajaxSettings),t):zt(w.ajaxSettings,e)},ajaxPrefilter:Ft(It),ajaxTransport:Ft(Wt),ajax:function(t,n){"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=w.ajaxSetup({},n),g=h.context||h,y=h.context&&(g.nodeType||g.jquery)?w(g):w.event,v=w.Deferred(),m=w.Callbacks("once memory"),x=h.statusCode||{},b={},T={},C="canceled",E={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s){s={};while(t=Ot.exec(a))s[t[1].toLowerCase()]=t[2]}t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=T[e.toLowerCase()]=T[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||C;return i&&i.abort(t),k(0,t),this}};if(v.promise(E),h.url=((t||h.url||Ct.href)+"").replace(Rt,Ct.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(M)||[""],null==h.crossDomain){l=r.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Bt.protocol+"//"+Bt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=w.param(h.data,h.traditional)),_t(It,h,n,E),c)return E;(f=w.event&&h.global)&&0==w.active++&&w.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Mt.test(h.type),o=h.url.replace(Lt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(qt,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(kt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Ht,"$1"),d=(kt.test(o)?"&":"?")+"_="+Et+++d),h.url=o+d),h.ifModified&&(w.lastModified[o]&&E.setRequestHeader("If-Modified-Since",w.lastModified[o]),w.etag[o]&&E.setRequestHeader("If-None-Match",w.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&E.setRequestHeader("Content-Type",h.contentType),E.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+$t+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)E.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,E,h)||c))return E.abort();if(C="abort",m.add(h.complete),E.done(h.success),E.fail(h.error),i=_t(Wt,h,n,E)){if(E.readyState=1,f&&y.trigger("ajaxSend",[E,h]),c)return E;h.async&&h.timeout>0&&(u=e.setTimeout(function(){E.abort("timeout")},h.timeout));try{c=!1,i.send(b,k)}catch(e){if(c)throw e;k(-1,e)}}else k(-1,"No Transport");function k(t,n,r,s){var l,p,d,b,T,C=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",E.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=Xt(h,E,r)),b=Ut(h,b,E,l),l?(h.ifModified&&((T=E.getResponseHeader("Last-Modified"))&&(w.lastModified[o]=T),(T=E.getResponseHeader("etag"))&&(w.etag[o]=T)),204===t||"HEAD"===h.type?C="nocontent":304===t?C="notmodified":(C=b.state,p=b.data,l=!(d=b.error))):(d=C,!t&&C||(C="error",t<0&&(t=0))),E.status=t,E.statusText=(n||C)+"",l?v.resolveWith(g,[p,C,E]):v.rejectWith(g,[E,C,d]),E.statusCode(x),x=void 0,f&&y.trigger(l?"ajaxSuccess":"ajaxError",[E,h,l?p:d]),m.fireWith(g,[E,C]),f&&(y.trigger("ajaxComplete",[E,h]),--w.active||w.event.trigger("ajaxStop")))}return E},getJSON:function(e,t,n){return w.get(e,t,n,"json")},getScript:function(e,t){return w.get(e,void 0,t,"script")}}),w.each(["get","post"],function(e,t){w[t]=function(e,n,r,i){return g(n)&&(i=i||r,r=n,n=void 0),w.ajax(w.extend({url:e,type:t,dataType:i,data:n,success:r},w.isPlainObject(e)&&e))}}),w._evalUrl=function(e){return w.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},w.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=w(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return g(e)?this.each(function(t){w(this).wrapInner(e.call(this,t))}):this.each(function(){var t=w(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=g(e);return this.each(function(n){w(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){w(this).replaceWith(this.childNodes)}),this}}),w.expr.pseudos.hidden=function(e){return!w.expr.pseudos.visible(e)},w.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},w.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Vt={0:200,1223:204},Gt=w.ajaxSettings.xhr();h.cors=!!Gt&&"withCredentials"in Gt,h.ajax=Gt=!!Gt,w.ajaxTransport(function(t){var n,r;if(h.cors||Gt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Vt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),w.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),w.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return w.globalEval(e),e}}}),w.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),w.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(i,o){t=w(" + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Classes

+

The following classes are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + HTTPClient + +
    +
    +
    +
    +
    +
    +

    HTTPClient class provides API for request execution.

    + +

    Example:

    +
        let client = HTTPClient(eventLoopGroupProvider = .createNew)
    +    client.get(url: "https://swift.org/", deadline: .now() + .seconds(1)).whenComplete { result in
    +        switch result {
    +        case .failure(let error):
    +            // process error
    +        case .success(let response):
    +            if let response.status == .ok {
    +                // handle response
    +            } else {
    +                // handle remote error
    +            }
    +        }
    +    }
    +
    + +

    It is important to close the client instance, for example in a defer statement, after use to cleanly shutdown the underlying NIO EventLoopGroup:

    +
        try client.syncShutdown()
    +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class HTTPClient
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + ResponseAccumulator + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class ResponseAccumulator : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Undocumented

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class HTTPClientCopyingDelegate : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient.html b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient.html new file mode 100644 index 000000000..6513067a6 --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient.html @@ -0,0 +1,1244 @@ + + + + HTTPClient Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClient

+
+
+
public class HTTPClient
+ +
+
+

HTTPClient class provides API for request execution.

+ +

Example:

+
    let client = HTTPClient(eventLoopGroupProvider = .createNew)
+    client.get(url: "https://swift.org/", deadline: .now() + .seconds(1)).whenComplete { result in
+        switch result {
+        case .failure(let error):
+            // process error
+        case .success(let response):
+            if let response.status == .ok {
+                // handle response
+            } else {
+                // handle remote error
+            }
+        }
+    }
+
+ +

It is important to close the client instance, for example in a defer statement, after use to cleanly shutdown the underlying NIO EventLoopGroup:

+
    try client.syncShutdown()
+
+ +
+
+ +
+
+
+
    +
  • +
    + + + + eventLoopGroup + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let eventLoopGroup: EventLoopGroup
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTPClient with specified EventLoopGroup provider and configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(eventLoopGroupProvider: EventLoopGroupProvider, configuration: Configuration = Configuration())
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + eventLoopGroupProvider + + +
    +

    Specify how EventLoopGroup will be created.

    +
    +
    + + configuration + + +
    +

    Client configuration.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + syncShutdown() + +
    +
    +
    +
    +
    +
    +

    Shuts down the client and EventLoopGroup if it was created by the client.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func syncShutdown() throws
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + get(url:deadline:) + +
    +
    +
    +
    +
    +
    +

    Execute GET request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func get(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute POST request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PATCH request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PUT request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + delete(url:deadline:) + +
    +
    +
    +
    +
    +
    +

    Execute DELETE request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func delete(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    The time when the request must have been completed by.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, eventLoop: EventLoopPreference, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          deadline: NIODeadline? = nil) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          eventLoop eventLoopPreference: EventLoopPreference,
    +                                                          deadline: NIODeadline? = nil) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + Configuration + +
    +
    +
    +
    +
    +
    +

    HTTPClient configuration.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Configuration
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum EventLoopGroupProvider
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + EventLoopPreference + +
    +
    +
    +
    +
    +
    +

    Specifies how the library will treat event loop passed by the user.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Decompression + +
    +
    +
    +
    +
    +
    +

    Specifies decompression settings.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum Decompression
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Cookie + +
    +
    +
    +
    +
    +
    +

    A representation of an HTTP cookie.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Cookie
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Represent HTTP response.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Response
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Body + +
    +
    +
    +
    +
    +
    +

    Represent request body.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Body
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Request + +
    +
    +
    +
    +
    +
    +

    Represent HTTP request.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Request
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Authorization + +
    +
    +
    +
    +
    +
    +

    HTTP authentication

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Authorization
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Task + +
    +
    +
    +
    +
    +
    +

    Response execution context. Will be created by the library and could be used for obtaining +EventLoopFuture<Response> of the execution or cancellation of the execution.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class Task<Response> : TaskProtocol
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Authorization.html b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Authorization.html new file mode 100644 index 000000000..caff45e68 --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Authorization.html @@ -0,0 +1,274 @@ + + + + Authorization Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Authorization

+
+
+
public struct Authorization
+ +
+
+

HTTP authentication

+ +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Body.html b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Body.html new file mode 100644 index 000000000..505ac945e --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Body.html @@ -0,0 +1,455 @@ + + + + Body Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Body

+
+
+
public struct Body
+ +
+
+

Represent request body.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + StreamWriter + +
    +
    +
    +
    +
    +
    +

    Chunk provider.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct StreamWriter
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + length + +
    +
    +
    +
    +
    +
    +

    Body size. Request validation will be failed with HTTPClientErrors.contentLengthMissing if nil, +unless Trasfer-Encoding: chunked header is set.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var length: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + stream + +
    +
    +
    +
    +
    +
    +

    Body chunk provider.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var stream: (StreamWriter) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + byteBuffer(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using ByteBuffer.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func byteBuffer(_ buffer: ByteBuffer) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + buffer + + +
    +

    Body ByteBuffer representation.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + stream(length:_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using StreamWriter.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func stream(length: Int? = nil, _ stream: @escaping (StreamWriter) -> EventLoopFuture<Void>) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + length + + +
    +

    Body size. Request validation will be failed with HTTPClientErrors.contentLengthMissing if nil, + unless Trasfer-Encoding: chunked header is set.

    +
    +
    + + stream + + +
    +

    Body chunk provider.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + data(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using Data.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func data(_ data: Data) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    Body Data representation.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + string(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using String.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func string(_ string: String) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + string + + +
    +

    Body String representation.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html new file mode 100644 index 000000000..c49cb8c5c --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html @@ -0,0 +1,252 @@ + + + + StreamWriter Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

StreamWriter

+
+
+
public struct StreamWriter
+ +
+
+

Chunk provider.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + init(closure:) + +
    +
    +
    +
    +
    +
    +

    Create new StreamWriter

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(closure: @escaping (IOData) -> EventLoopFuture<Void>)
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + closure + + +
    +

    function that will be called to write actual bytes to the channel.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + write(_:) + +
    +
    +
    +
    +
    +
    +

    Write data to server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func write(_ data: IOData) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    IOData to write.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Configuration.html b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Configuration.html new file mode 100644 index 000000000..02609502e --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Configuration.html @@ -0,0 +1,518 @@ + + + + Configuration Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Configuration

+
+
+
public struct Configuration
+ +
+
+

HTTPClient configuration.

+ +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html new file mode 100644 index 000000000..81c49c5c9 --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html @@ -0,0 +1,387 @@ + + + + Proxy Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Proxy

+
+
+
struct Proxy
+ +
+
+

Proxy server configuration +Specifies the remote address of an HTTP proxy.

+ +

Adding an Proxy to your client’s HTTPClient.Configuration +will cause requests to be passed through the specified proxy using the +HTTP CONNECT method.

+ +

If a TLSConfiguration is used in conjunction with HTTPClient.Configuration.Proxy, +TLS will be established after successful proxy, between your client +and the destination server.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server host.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + port + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server port.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var port: Int
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + authorization + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server authorization.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var authorization: HTTPClient.Authorization?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + server(host:port:) + +
    +
    +
    +
    +
    +
    +

    Create proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func server(host: String, port: Int) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + host + + +
    +

    proxy server host.

    +
    +
    + + port + + +
    +

    proxy server port.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func server(host: String, port: Int, authorization: HTTPClient.Authorization? = nil) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + host + + +
    +

    proxy server host.

    +
    +
    + + port + + +
    +

    proxy server port.

    +
    +
    + + authorization + + +
    +

    proxy server authorization.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html new file mode 100644 index 000000000..ae921bcd9 --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html @@ -0,0 +1,250 @@ + + + + RedirectConfiguration Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

RedirectConfiguration

+
+
+
public struct RedirectConfiguration
+ +
+
+

Specifies redirect processing settings.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + disallow + +
    +
    +
    +
    +
    +
    +

    Redirects are not followed.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let disallow: HTTPClient.Configuration.RedirectConfiguration
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Redirects are followed with a specified limit.

    +
    +

    Warning

    +

    Cycle detection will keep all visited URLs in memory which means a malicious server could use this as a denial-of-service vector.

    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func follow(max: Int, allowCycles: Bool) -> RedirectConfiguration
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + max + + +
    +

    The maximum number of allowed redirects.

    +
    +
    + + allowCycles + + +
    +

    Whether cycles are allowed.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html new file mode 100644 index 000000000..19f6e1c03 --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html @@ -0,0 +1,275 @@ + + + + Timeout Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Timeout

+
+
+
public struct Timeout
+ +
+
+

Timeout configuration

+ +
+
+ +
+
+
+
    +
  • +
    + + + + connect + +
    +
    +
    +
    +
    +
    +

    Specifies connect timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var connect: TimeAmount?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + read + +
    +
    +
    +
    +
    +
    +

    Specifies read timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var read: TimeAmount?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + init(connect:read:) + +
    +
    +
    +
    +
    +
    +

    Create timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(connect: TimeAmount? = nil, read: TimeAmount? = nil)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + connect + + +
    +

    connect timeout.

    +
    +
    + + read + + +
    +

    read timeout.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Cookie.html b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Cookie.html new file mode 100644 index 000000000..c6bba3e76 --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Cookie.html @@ -0,0 +1,592 @@ + + + + Cookie Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Cookie

+
+
+
public struct Cookie
+ +
+
+

A representation of an HTTP cookie.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + name + +
    +
    +
    +
    +
    +
    +

    The name of the cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var name: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + value + +
    +
    +
    +
    +
    +
    +

    The cookie’s string value.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var value: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + path + +
    +
    +
    +
    +
    +
    +

    The cookie’s path.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var path: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + domain + +
    +
    +
    +
    +
    +
    +

    The domain of the cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var domain: String?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + expires + +
    +
    +
    +
    +
    +
    +

    The cookie’s expiration date.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var expires: Date?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + maxAge + +
    +
    +
    +
    +
    +
    +

    The cookie’s age in seconds.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var maxAge: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + httpOnly + +
    +
    +
    +
    +
    +
    +

    Whether the cookie should only be sent to HTTP servers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var httpOnly: Bool
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + secure + +
    +
    +
    +
    +
    +
    +

    Whether the cookie should only be sent over secure channels.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var secure: Bool
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a Cookie by parsing a Set-Cookie header.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(header: String, defaultDomain: String)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + header + + +
    +

    String representation of the Set-Cookie response header.

    +
    +
    + + defaultDomain + + +
    +

    Default domain to use if cookie was sent without one.

    +
    +
    +
    +
    +

    Return Value

    +

    nil if the header is invalid.

    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(name: String, value: String, path: String = "/", domain: String? = nil, expires: Date? = nil, maxAge: Int? = nil, httpOnly: Bool = false, secure: Bool = false)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + name + + +
    +

    The name of the cookie.

    +
    +
    + + value + + +
    +

    The cookie’s string value.

    +
    +
    + + path + + +
    +

    The cookie’s path.

    +
    +
    + + domain + + +
    +

    The domain of the cookie, defaults to nil.

    +
    +
    + + expires + + +
    +

    The cookie’s expiration date, defaults to nil.

    +
    +
    + + maxAge + + +
    +

    The cookie’s age in seconds, defaults to nil.

    +
    +
    + + httpOnly + + +
    +

    Whether this cookie should be used by HTTP servers only, defaults to false.

    +
    +
    + + secure + + +
    +

    Whether this cookie should only be sent using secure channels, defaults to false.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Decompression.html b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Decompression.html new file mode 100644 index 000000000..62dd13fb7 --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Decompression.html @@ -0,0 +1,214 @@ + + + + Decompression Enumeration Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Decompression

+
+
+
public enum Decompression
+ +
+
+

Specifies decompression settings.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + disabled + +
    +
    +
    +
    +
    +
    +

    Decompression is disabled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case disabled
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + enabled(limit:) + +
    +
    +
    +
    +
    +
    +

    Decompression is enabled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case enabled(limit: NIOHTTPDecompression.DecompressionLimit)
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html new file mode 100644 index 000000000..aa0b74243 --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html @@ -0,0 +1,214 @@ + + + + EventLoopGroupProvider Enumeration Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

EventLoopGroupProvider

+
+
+
public enum EventLoopGroupProvider
+ +
+
+

Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + shared(_:) + +
    +
    +
    +
    +
    +
    +

    EventLoopGroup will be provided by the user. Owner of this group is responsible for its lifecycle.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case shared(EventLoopGroup)
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + createNew + +
    +
    +
    +
    +
    +
    +

    EventLoopGroup will be created by the client. When syncShutdown is called, created EventLoopGroup will be shut down as well.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case createNew
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html new file mode 100644 index 000000000..53f0e9f69 --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html @@ -0,0 +1,251 @@ + + + + EventLoopPreference Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

EventLoopPreference

+
+
+
public struct EventLoopPreference
+ +
+
+

Specifies how the library will treat event loop passed by the user.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + indifferent + +
    +
    +
    +
    +
    +
    +

    Event Loop will be selected by the library.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let indifferent: HTTPClient.EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + delegate(on:) + +
    +
    +
    +
    +
    +
    +

    The delegate will be run on the specified EventLoop (and the Channel if possible).

    + +

    This will call the configured delegate on eventLoop and will try to use a Channel on the same +EventLoop but will not establish a new network connection just to satisfy the EventLoop preference if +another existing connection on a different EventLoop is readily available from a connection pool.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func delegate(on eventLoop: EventLoop) -> EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The delegate and the Channel will be run on the specified EventLoop.

    + +

    Use this for use-cases where you prefer a new connection to be established over re-using an existing +connection that might be on a different EventLoop.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func delegateAndChannel(on eventLoop: EventLoop) -> EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Request.html b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Request.html new file mode 100644 index 000000000..d8bf2c3f9 --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Request.html @@ -0,0 +1,595 @@ + + + + Request Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Request

+
+
+
public struct Request
+ +
+
+

Represent HTTP request.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + method + +
    +
    +
    +
    +
    +
    +

    Request HTTP method, defaults to GET.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let method: HTTPMethod
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + url + +
    +
    +
    +
    +
    +
    +

    Remote URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let url: URL
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + scheme + +
    +
    +
    +
    +
    +
    +

    Remote HTTP scheme, resolved from URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let scheme: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Remote host, resolved from URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + headers + +
    +
    +
    +
    +
    +
    +

    Request custom HTTP Headers, defaults to no headers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var headers: HTTPHeaders
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + body + +
    +
    +
    +
    +
    +
    +

    Request body, defaults to no body.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var body: HTTPClient.Body?
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP request.

    +
    +

    Throws

    +
      +
    • invalidURL if URL cannot be parsed.
    • +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: String, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + version + + +
    +

    HTTP version.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTP Request.

    +
    +

    Throws

    +
      +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + useTLS + +
    +
    +
    +
    +
    +
    +

    Whether request will be executed using secure socket.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var useTLS: Bool { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + port + +
    +
    +
    +
    +
    +
    +

    Resolved port.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var port: Int { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Response.html b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Response.html new file mode 100644 index 000000000..f58fe347e --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Response.html @@ -0,0 +1,389 @@ + + + + Response Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Response

+
+
+
public struct Response
+ +
+
+

Represent HTTP response.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Remote host of the request.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + status + +
    +
    +
    +
    +
    +
    +

    Response HTTP status.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var status: HTTPResponseStatus
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + headers + +
    +
    +
    +
    +
    +
    +

    Reponse HTTP headers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var headers: HTTPHeaders
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + body + +
    +
    +
    +
    +
    +
    +

    Response body.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var body: ByteBuffer?
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP Response.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(host: String, status: HTTPResponseStatus, headers: HTTPHeaders, body: ByteBuffer?)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + host + + +
    +

    Remote host of the request.

    +
    +
    + + status + + +
    +

    Response HTTP status.

    +
    +
    + + headers + + +
    +

    Reponse HTTP headers.

    +
    +
    + + body + + +
    +

    Response body.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + cookies + +
    +
    +
    +
    +
    +
    +

    List of HTTP cookies returned by the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var cookies: [HTTPClient.Cookie] { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Task.html b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Task.html new file mode 100644 index 000000000..ee497142d --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClient/Task.html @@ -0,0 +1,284 @@ + + + + Task Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Task

+
+
+
public final class Task<Response> : TaskProtocol
+ +
+
+

Response execution context. Will be created by the library and could be used for obtaining +EventLoopFuture<Response> of the execution or cancellation of the execution.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + eventLoop + +
    +
    +
    +
    +
    +
    +

    The EventLoop the delegate will be executed on.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let eventLoop: EventLoop
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + futureResult + +
    +
    +
    +
    +
    +
    +

    EventLoopFuture for the response returned by this request.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var futureResult: EventLoopFuture<Response> { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + wait() + +
    +
    +
    +
    +
    +
    +

    Waits for execution of this request to complete.

    +
    +

    Throws

    + The error value of the EventLoopFuture if it errors. + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func wait() throws -> Response
    + +
    +
    +
    +

    Return Value

    +

    The value of the EventLoopFuture when it completes.

    +
    + +
    +
    +
  • +
  • +
    + + + + cancel() + +
    +
    +
    +
    +
    +
    +

    Cancels the request execution.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func cancel()
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html new file mode 100644 index 000000000..1ef617692 --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html @@ -0,0 +1,272 @@ + + + + HTTPClientCopyingDelegate Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientCopyingDelegate

+
+
+
public final class HTTPClientCopyingDelegate : HTTPClientResponseDelegate
+ +
+
+

Undocumented

+ +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/Classes/ResponseAccumulator.html b/docs/1.1.0/AsyncHTTPClient/Classes/ResponseAccumulator.html new file mode 100644 index 000000000..cab2c4694 --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/Classes/ResponseAccumulator.html @@ -0,0 +1,330 @@ + + + + ResponseAccumulator Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

ResponseAccumulator

+
+
+
public class ResponseAccumulator : HTTPClientResponseDelegate
+ +
+
+

Undocumented

+ +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/Protocols.html b/docs/1.1.0/AsyncHTTPClient/Protocols.html new file mode 100644 index 000000000..5767ebb6a --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/Protocols.html @@ -0,0 +1,191 @@ + + + + Protocols Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Protocols

+

The following protocols are available globally.

+ +
+
+ +
+
+
+
    +
  • + +
    +
    +
    +
    +
    +

    HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed. +You can implement this protocol if you need fine-grained control over an HTTP request/response, for example, if you want to inspect the response +headers before deciding whether to accept a response body, or if you want to stream your request body. Pass an instance of your conforming +class to the HTTPClient.execute() method and this package will call each delegate method appropriately as the request takes place.

    +
    +

    Note

    + This delegate is strongly held by the HTTPTaskHandler + for the duration of the Request processing and will be + released together with the HTTPTaskHandler when channel is closed. + Users of the library are not required to keep a reference to the + object that implements this protocol, but may do so if needed. + +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public protocol HTTPClientResponseDelegate : AnyObject
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html b/docs/1.1.0/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html new file mode 100644 index 000000000..894d7c4d4 --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html @@ -0,0 +1,657 @@ + + + + HTTPClientResponseDelegate Protocol Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientResponseDelegate

+
+
+
public protocol HTTPClientResponseDelegate : AnyObject
+ +
+
+

HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed. +You can implement this protocol if you need fine-grained control over an HTTP request/response, for example, if you want to inspect the response +headers before deciding whether to accept a response body, or if you want to stream your request body. Pass an instance of your conforming +class to the HTTPClient.execute() method and this package will call each delegate method appropriately as the request takes place.

+
+

Note

+ This delegate is strongly held by the HTTPTaskHandler + for the duration of the Request processing and will be + released together with the HTTPTaskHandler when channel is closed. + Users of the library are not required to keep a reference to the + object that implements this protocol, but may do so if needed. + +
+ +
+
+ +
+
+
+
    +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    associatedtype Response
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequestHead(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when the request head is sent. Will be called once.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequestHead(task: HTTPClient.Task<Response>, _ head: HTTPRequestHead)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + head + + +
    +

    Request head.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequestPart(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when a part of the request body is sent. Could be called zero or more times.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequestPart(task: HTTPClient.Task<Response>, _ part: IOData)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + part + + +
    +

    Request body Part.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequest(task:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when the request is fully sent. Will be called once.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequest(task: HTTPClient.Task<Response>)
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveHead(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when response head is received. Will be called once. +You must return an EventLoopFuture<Void> that you complete when you have finished processing the body part. +You can create an already succeeded future by calling task.eventLoop.makeSucceededFuture(()).

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveHead(task: HTTPClient.Task<Response>, _ head: HTTPResponseHead) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + head + + +
    +

    Received reposonse head.

    +
    +
    +
    +
    +

    Return Value

    +

    EventLoopFuture that will be used for backpressure.

    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveBodyPart(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when part of a response body is received. Could be called zero or more times. +You must return an EventLoopFuture<Void> that you complete when you have finished processing the body part. +You can create an already succeeded future by calling task.eventLoop.makeSucceededFuture(()).

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveBodyPart(task: HTTPClient.Task<Response>, _ buffer: ByteBuffer) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + buffer + + +
    +

    Received body Part.

    +
    +
    +
    +
    +

    Return Value

    +

    EventLoopFuture that will be used for backpressure.

    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveError(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when error was thrown during request execution. Will be called zero or one time only. Request processing will be stopped after that.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveError(task: HTTPClient.Task<Response>, _ error: Error)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + error + + +
    +

    Error that occured during response processing.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Called when the complete HTTP request is finished. You must return an instance of your Response associated type. Will be called once, except if an error occurred.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didFinishRequest(task: HTTPClient.Task<Response>) throws -> Response
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    +
    +
    +

    Return Value

    +

    Result of processing.

    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/Structs.html b/docs/1.1.0/AsyncHTTPClient/Structs.html new file mode 100644 index 000000000..c86b4d164 --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/Structs.html @@ -0,0 +1,179 @@ + + + + Structures Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Structures

+

The following structures are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + HTTPClientError + +
    +
    +
    +
    +
    +
    +

    Possible client errors.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct HTTPClientError : Error, Equatable, CustomStringConvertible
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/Structs/HTTPClientError.html b/docs/1.1.0/AsyncHTTPClient/Structs/HTTPClientError.html new file mode 100644 index 000000000..a9b690a1a --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/Structs/HTTPClientError.html @@ -0,0 +1,663 @@ + + + + HTTPClientError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientError

+
+
+
public struct HTTPClientError : Error, Equatable, CustomStringConvertible
+ +
+
+

Possible client errors.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var description: String { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + invalidURL + +
    +
    +
    +
    +
    +
    +

    URL provided is invalid.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let invalidURL: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + emptyHost + +
    +
    +
    +
    +
    +
    +

    URL does not contain host.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let emptyHost: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + alreadyShutdown + +
    +
    +
    +
    +
    +
    +

    Client is shutdown and cannot be used for new requests.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let alreadyShutdown: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + emptyScheme + +
    +
    +
    +
    +
    +
    +

    URL does not contain scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let emptyScheme: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + unsupportedScheme(_:) + +
    +
    +
    +
    +
    +
    +

    Provided URL scheme is not supported, supported schemes are: http and https

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func unsupportedScheme(_ scheme: String) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + readTimeout + +
    +
    +
    +
    +
    +
    +

    Request timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let readTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Remote connection was closed unexpectedly.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let remoteConnectionClosed: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + cancelled + +
    +
    +
    +
    +
    +
    +

    Request was cancelled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let cancelled: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Request contains invalid identity encoding.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let identityCodingIncorrectlyPresent: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Request contains multiple chunks definitions.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let chunkedSpecifiedMultipleTimes: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + invalidProxyResponse + +
    +
    +
    +
    +
    +
    +

    Proxy response was invalid.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let invalidProxyResponse: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + contentLengthMissing + +
    +
    +
    +
    +
    +
    +

    Request does not contain Content-Length header.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let contentLengthMissing: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Proxy Authentication Required.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let proxyAuthenticationRequired: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + redirectLimitReached + +
    +
    +
    +
    +
    +
    +

    Redirect Limit reached.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let redirectLimitReached: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + redirectCycleDetected + +
    +
    +
    +
    +
    +
    +

    Redirect Cycle detected.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let redirectCycleDetected: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + uncleanShutdown + +
    +
    +
    +
    +
    +
    +

    Unclean shutdown

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let uncleanShutdown: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/badge.svg b/docs/1.1.0/AsyncHTTPClient/badge.svg new file mode 100644 index 000000000..afb579661 --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/badge.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + documentation + + + documentation + + + 90% + + + 90% + + + diff --git a/docs/1.1.0/AsyncHTTPClient/css/highlight.css b/docs/1.1.0/AsyncHTTPClient/css/highlight.css new file mode 100644 index 000000000..d0db0e13b --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/css/highlight.css @@ -0,0 +1,200 @@ +/* Credit to https://gist.github.com/wataru420/2048287 */ +.highlight { + /* Comment */ + /* Error */ + /* Keyword */ + /* Operator */ + /* Comment.Multiline */ + /* Comment.Preproc */ + /* Comment.Single */ + /* Comment.Special */ + /* Generic.Deleted */ + /* Generic.Deleted.Specific */ + /* Generic.Emph */ + /* Generic.Error */ + /* Generic.Heading */ + /* Generic.Inserted */ + /* Generic.Inserted.Specific */ + /* Generic.Output */ + /* Generic.Prompt */ + /* Generic.Strong */ + /* Generic.Subheading */ + /* Generic.Traceback */ + /* Keyword.Constant */ + /* Keyword.Declaration */ + /* Keyword.Pseudo */ + /* Keyword.Reserved */ + /* Keyword.Type */ + /* Literal.Number */ + /* Literal.String */ + /* Name.Attribute */ + /* Name.Builtin */ + /* Name.Class */ + /* Name.Constant */ + /* Name.Entity */ + /* Name.Exception */ + /* Name.Function */ + /* Name.Namespace */ + /* Name.Tag */ + /* Name.Variable */ + /* Operator.Word */ + /* Text.Whitespace */ + /* Literal.Number.Float */ + /* Literal.Number.Hex */ + /* Literal.Number.Integer */ + /* Literal.Number.Oct */ + /* Literal.String.Backtick */ + /* Literal.String.Char */ + /* Literal.String.Doc */ + /* Literal.String.Double */ + /* Literal.String.Escape */ + /* Literal.String.Heredoc */ + /* Literal.String.Interpol */ + /* Literal.String.Other */ + /* Literal.String.Regex */ + /* Literal.String.Single */ + /* Literal.String.Symbol */ + /* Name.Builtin.Pseudo */ + /* Name.Variable.Class */ + /* Name.Variable.Global */ + /* Name.Variable.Instance */ + /* Literal.Number.Integer.Long */ } + .highlight .c { + color: #999988; + font-style: italic; } + .highlight .err { + color: #a61717; + background-color: #e3d2d2; } + .highlight .k { + color: #000000; + font-weight: bold; } + .highlight .o { + color: #000000; + font-weight: bold; } + .highlight .cm { + color: #999988; + font-style: italic; } + .highlight .cp { + color: #999999; + font-weight: bold; } + .highlight .c1 { + color: #999988; + font-style: italic; } + .highlight .cs { + color: #999999; + font-weight: bold; + font-style: italic; } + .highlight .gd { + color: #000000; + background-color: #ffdddd; } + .highlight .gd .x { + color: #000000; + background-color: #ffaaaa; } + .highlight .ge { + color: #000000; + font-style: italic; } + .highlight .gr { + color: #aa0000; } + .highlight .gh { + color: #999999; } + .highlight .gi { + color: #000000; + background-color: #ddffdd; } + .highlight .gi .x { + color: #000000; + background-color: #aaffaa; } + .highlight .go { + color: #888888; } + .highlight .gp { + color: #555555; } + .highlight .gs { + font-weight: bold; } + .highlight .gu { + color: #aaaaaa; } + .highlight .gt { + color: #aa0000; } + .highlight .kc { + color: #000000; + font-weight: bold; } + .highlight .kd { + color: #000000; + font-weight: bold; } + .highlight .kp { + color: #000000; + font-weight: bold; } + .highlight .kr { + color: #000000; + font-weight: bold; } + .highlight .kt { + color: #445588; } + .highlight .m { + color: #009999; } + .highlight .s { + color: #d14; } + .highlight .na { + color: #008080; } + .highlight .nb { + color: #0086B3; } + .highlight .nc { + color: #445588; + font-weight: bold; } + .highlight .no { + color: #008080; } + .highlight .ni { + color: #800080; } + .highlight .ne { + color: #990000; + font-weight: bold; } + .highlight .nf { + color: #990000; } + .highlight .nn { + color: #555555; } + .highlight .nt { + color: #000080; } + .highlight .nv { + color: #008080; } + .highlight .ow { + color: #000000; + font-weight: bold; } + .highlight .w { + color: #bbbbbb; } + .highlight .mf { + color: #009999; } + .highlight .mh { + color: #009999; } + .highlight .mi { + color: #009999; } + .highlight .mo { + color: #009999; } + .highlight .sb { + color: #d14; } + .highlight .sc { + color: #d14; } + .highlight .sd { + color: #d14; } + .highlight .s2 { + color: #d14; } + .highlight .se { + color: #d14; } + .highlight .sh { + color: #d14; } + .highlight .si { + color: #d14; } + .highlight .sx { + color: #d14; } + .highlight .sr { + color: #009926; } + .highlight .s1 { + color: #d14; } + .highlight .ss { + color: #990073; } + .highlight .bp { + color: #999999; } + .highlight .vc { + color: #008080; } + .highlight .vg { + color: #008080; } + .highlight .vi { + color: #008080; } + .highlight .il { + color: #009999; } diff --git a/docs/1.1.0/AsyncHTTPClient/css/jazzy.css b/docs/1.1.0/AsyncHTTPClient/css/jazzy.css new file mode 100644 index 000000000..3ca6b3173 --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/css/jazzy.css @@ -0,0 +1,396 @@ +*, *:before, *:after { + box-sizing: inherit; } + +body { + margin: 0; + background: #fff; + color: #333; + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + letter-spacing: .2px; + -webkit-font-smoothing: antialiased; + box-sizing: border-box; } + +h1 { + font-size: 2rem; + font-weight: 700; + margin: 1.275em 0 0.6em; } + +h2 { + font-size: 1.75rem; + font-weight: 700; + margin: 1.275em 0 0.3em; } + +h3 { + font-size: 1.5rem; + font-weight: 700; + margin: 1em 0 0.3em; } + +h4 { + font-size: 1.25rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h5 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h6 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; + color: #777; } + +p { + margin: 0 0 1em; } + +ul, ol { + padding: 0 0 0 2em; + margin: 0 0 0.85em; } + +blockquote { + margin: 0 0 0.85em; + padding: 0 15px; + color: #858585; + border-left: 4px solid #e5e5e5; } + +img { + max-width: 100%; } + +a { + color: #4183c4; + text-decoration: none; } + a:hover, a:focus { + outline: 0; + text-decoration: underline; } + a.discouraged { + text-decoration: line-through; } + a.discouraged:hover, a.discouraged:focus { + text-decoration: underline line-through; } + +table { + background: #fff; + width: 100%; + border-collapse: collapse; + border-spacing: 0; + overflow: auto; + margin: 0 0 0.85em; } + +tr:nth-child(2n) { + background-color: #fbfbfb; } + +th, td { + padding: 6px 13px; + border: 1px solid #ddd; } + +pre { + margin: 0 0 1.275em; + padding: .85em 1em; + overflow: auto; + background: #f7f7f7; + font-size: .85em; + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +code { + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +.item-container p > code, .item-container li > code, .top-matter p > code, .top-matter li > code { + background: #f7f7f7; + padding: .2em; } + .item-container p > code:before, .item-container p > code:after, .item-container li > code:before, .item-container li > code:after, .top-matter p > code:before, .top-matter p > code:after, .top-matter li > code:before, .top-matter li > code:after { + letter-spacing: -.2em; + content: "\00a0"; } + +pre code { + padding: 0; + white-space: pre; } + +.content-wrapper { + display: flex; + flex-direction: column; } + @media (min-width: 768px) { + .content-wrapper { + flex-direction: row; } } + +.header { + display: flex; + padding: 8px; + font-size: 0.875em; + background: #444; + color: #999; } + +.header-col { + margin: 0; + padding: 0 8px; } + +.header-col--primary { + flex: 1; } + +.header-link { + color: #fff; } + +.header-icon { + padding-right: 6px; + vertical-align: -4px; + height: 16px; } + +.breadcrumbs { + font-size: 0.875em; + padding: 8px 16px; + margin: 0; + background: #fbfbfb; + border-bottom: 1px solid #ddd; } + +.carat { + height: 10px; + margin: 0 5px; } + +.navigation { + order: 2; } + @media (min-width: 768px) { + .navigation { + order: 1; + width: 25%; + max-width: 300px; + padding-bottom: 64px; + overflow: hidden; + word-wrap: normal; + background: #fbfbfb; + border-right: 1px solid #ddd; } } + +.nav-groups { + list-style-type: none; + padding-left: 0; } + +.nav-group-name { + border-bottom: 1px solid #ddd; + padding: 8px 0 8px 16px; } + +.nav-group-name-link { + color: #333; } + +.nav-group-tasks { + margin: 8px 0; + padding: 0 0 0 8px; } + +.nav-group-task { + font-size: 1em; + list-style-type: none; + white-space: nowrap; } + +.nav-group-task-link { + color: #808080; } + +.main-content { + order: 1; } + @media (min-width: 768px) { + .main-content { + order: 2; + flex: 1; + padding-bottom: 60px; } } + +.section { + padding: 0 32px; + border-bottom: 1px solid #ddd; } + +.section-content { + max-width: 834px; + margin: 0 auto; + padding: 16px 0; } + +.section-name { + color: #666; + display: block; } + .section-name p { + margin-bottom: inherit; } + +.declaration .highlight { + overflow-x: initial; + padding: 8px 0; + margin: 0; + background-color: transparent; + border: none; } + +.task-group-section { + border-top: 1px solid #ddd; } + +.task-group { + padding-top: 0px; } + +.task-name-container a[name]:before { + content: ""; + display: block; } + +.section-name-container { + position: relative; } + .section-name-container .section-name-link { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + margin-bottom: 0; } + .section-name-container .section-name { + position: relative; + pointer-events: none; + z-index: 1; } + .section-name-container .section-name a { + pointer-events: auto; } + +.item-container { + padding: 0; } + +.item { + padding-top: 8px; + width: 100%; + list-style-type: none; } + .item a[name]:before { + content: ""; + display: block; } + .item .token, .item .direct-link { + padding-left: 3px; + margin-left: 0px; + font-size: 1rem; } + .item .declaration-note { + font-size: .85em; + color: #808080; + font-style: italic; } + +.pointer-container { + border-bottom: 1px solid #ddd; + left: -23px; + padding-bottom: 13px; + position: relative; + width: 110%; } + +.pointer { + left: 21px; + top: 7px; + display: block; + position: absolute; + width: 12px; + height: 12px; + border-left: 1px solid #ddd; + border-top: 1px solid #ddd; + background: #fff; + transform: rotate(45deg); } + +.height-container { + display: none; + position: relative; + width: 100%; + overflow: hidden; } + .height-container .section { + background: #fff; + border: 1px solid #ddd; + border-top-width: 0; + padding-top: 10px; + padding-bottom: 5px; + padding: 8px 16px; } + +.aside, .language { + padding: 6px 12px; + margin: 12px 0; + border-left: 5px solid #dddddd; + overflow-y: hidden; } + .aside .aside-title, .language .aside-title { + font-size: 9px; + letter-spacing: 2px; + text-transform: uppercase; + padding-bottom: 0; + margin: 0; + color: #aaa; + -webkit-user-select: none; } + .aside p:last-child, .language p:last-child { + margin-bottom: 0; } + +.language { + border-left: 5px solid #cde9f4; } + .language .aside-title { + color: #4183c4; } + +.aside-warning, .aside-deprecated, .aside-unavailable { + border-left: 5px solid #ff6666; } + .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { + color: #ff0000; } + +.graybox { + border-collapse: collapse; + width: 100%; } + .graybox p { + margin: 0; + word-break: break-word; + min-width: 50px; } + .graybox td { + border: 1px solid #ddd; + padding: 5px 25px 5px 10px; + vertical-align: middle; } + .graybox tr td:first-of-type { + text-align: right; + padding: 7px; + vertical-align: top; + word-break: normal; + width: 40px; } + +.slightly-smaller { + font-size: 0.9em; } + +.footer { + padding: 8px 16px; + background: #444; + color: #ddd; + font-size: 0.8em; } + .footer p { + margin: 8px 0; } + .footer a { + color: #fff; } + +html.dash .header, html.dash .breadcrumbs, html.dash .navigation { + display: none; } + +html.dash .height-container { + display: block; } + +form[role=search] input { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 24px; + padding: 0 10px; + margin: 0; + border: none; + border-radius: 1em; } + .loading form[role=search] input { + background: white url(http://github.com/img/spinner.gif) center right 4px no-repeat; } + +form[role=search] .tt-menu { + margin: 0; + min-width: 300px; + background: #fbfbfb; + color: #333; + border: 1px solid #ddd; } + +form[role=search] .tt-highlight { + font-weight: bold; } + +form[role=search] .tt-suggestion { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 0 8px; } + form[role=search] .tt-suggestion span { + display: table-cell; + white-space: nowrap; } + form[role=search] .tt-suggestion .doc-parent-name { + width: 100%; + text-align: right; + font-weight: normal; + font-size: 0.9em; + padding-left: 16px; } + +form[role=search] .tt-suggestion:hover, +form[role=search] .tt-suggestion.tt-cursor { + cursor: pointer; + background-color: #4183c4; + color: #fff; } + +form[role=search] .tt-suggestion:hover .doc-parent-name, +form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { + color: #fff; } diff --git a/docs/1.1.0/AsyncHTTPClient/img/carat.png b/docs/1.1.0/AsyncHTTPClient/img/carat.png new file mode 100755 index 000000000..29d2f7fd4 Binary files /dev/null and b/docs/1.1.0/AsyncHTTPClient/img/carat.png differ diff --git a/docs/1.1.0/AsyncHTTPClient/img/dash.png b/docs/1.1.0/AsyncHTTPClient/img/dash.png new file mode 100755 index 000000000..6f694c7a0 Binary files /dev/null and b/docs/1.1.0/AsyncHTTPClient/img/dash.png differ diff --git a/docs/1.1.0/AsyncHTTPClient/img/gh.png b/docs/1.1.0/AsyncHTTPClient/img/gh.png new file mode 100755 index 000000000..628da97c7 Binary files /dev/null and b/docs/1.1.0/AsyncHTTPClient/img/gh.png differ diff --git a/docs/1.1.0/AsyncHTTPClient/img/spinner.gif b/docs/1.1.0/AsyncHTTPClient/img/spinner.gif new file mode 100644 index 000000000..e3038d0a4 Binary files /dev/null and b/docs/1.1.0/AsyncHTTPClient/img/spinner.gif differ diff --git a/docs/1.1.0/AsyncHTTPClient/index.html b/docs/1.1.0/AsyncHTTPClient/index.html new file mode 100644 index 000000000..ba646de4d --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/index.html @@ -0,0 +1,145 @@ + + + + AsyncHTTPClient Reference + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.1.0 Docs + + (90% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+ +

AsyncHTTPClient Docs

+ +

AsyncHTTPClient is a Swift HTTP Client package.

+ +

To get started with AsyncHTTPClient, import AsyncHTTPClient. The +most important type is HTTPClient +which you can use to emit log messages.

+ +
+
+ + +
+
+ + + + diff --git a/docs/1.1.0/AsyncHTTPClient/js/jazzy.js b/docs/1.1.0/AsyncHTTPClient/js/jazzy.js new file mode 100755 index 000000000..c31dc05e4 --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/js/jazzy.js @@ -0,0 +1,59 @@ +window.jazzy = {'docset': false} +if (typeof window.dash != 'undefined') { + document.documentElement.className += ' dash' + window.jazzy.docset = true +} +if (navigator.userAgent.match(/xcode/i)) { + document.documentElement.className += ' xcode' + window.jazzy.docset = true +} + +function toggleItem($link, $content) { + var animationDuration = 300; + $link.toggleClass('token-open'); + $content.slideToggle(animationDuration); +} + +function itemLinkToContent($link) { + return $link.parent().parent().next(); +} + +// On doc load + hash-change, open any targetted item +function openCurrentItemIfClosed() { + if (window.jazzy.docset) { + return; + } + var $link = $(`.token[href="http://github.com/$%7Blocation.hash%7D"]`); + $content = itemLinkToContent($link); + if ($content.is(':hidden')) { + toggleItem($link, $content); + } +} + +$(openCurrentItemIfClosed); +$(window).on('hashchange', openCurrentItemIfClosed); + +// On item link ('token') click, toggle its discussion +$('.token').on('click', function(event) { + if (window.jazzy.docset) { + return; + } + var $link = $(this); + toggleItem($link, itemLinkToContent($link)); + + // Keeps the document from jumping to the hash. + var href = $link.attr('href'); + if (history.pushState) { + history.pushState({}, '', href); + } else { + location.hash = href; + } + event.preventDefault(); +}); + +// Clicks on links to the current, closed, item need to open the item +$("a:not('.token')").on('click', function() { + if (location == this.href) { + openCurrentItemIfClosed(); + } +}); diff --git a/docs/1.1.0/AsyncHTTPClient/js/jazzy.search.js b/docs/1.1.0/AsyncHTTPClient/js/jazzy.search.js new file mode 100644 index 000000000..e3d1ab905 --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/js/jazzy.search.js @@ -0,0 +1,70 @@ +$(function(){ + var $typeahead = $('[data-typeahead]'); + var $form = $typeahead.parents('form'); + var searchURL = $form.attr('action'); + + function displayTemplate(result) { + return result.name; + } + + function suggestionTemplate(result) { + var t = '
'; + t += '' + result.name + ''; + if (result.parent_name) { + t += '' + result.parent_name + ''; + } + t += '
'; + return t; + } + + $typeahead.one('focus', function() { + $form.addClass('loading'); + + $.getJSON(searchURL).then(function(searchData) { + const searchIndex = lunr(function() { + this.ref('url'); + this.field('name'); + this.field('abstract'); + for (const [url, doc] of Object.entries(searchData)) { + this.add({url: url, name: doc.name, abstract: doc.abstract}); + } + }); + + $typeahead.typeahead( + { + highlight: true, + minLength: 3, + autoselect: true + }, + { + limit: 10, + display: displayTemplate, + templates: { suggestion: suggestionTemplate }, + source: function(query, sync) { + const lcSearch = query.toLowerCase(); + const results = searchIndex.query(function(q) { + q.term(lcSearch, { boost: 100 }); + q.term(lcSearch, { + boost: 10, + wildcard: lunr.Query.wildcard.TRAILING + }); + }).map(function(result) { + var doc = searchData[result.ref]; + doc.url = result.ref; + return doc; + }); + sync(results); + } + } + ); + $form.removeClass('loading'); + $typeahead.trigger('focus'); + }); + }); + + var baseURL = searchURL.slice(0, -"search.json".length); + + $typeahead.on('typeahead:select', function(e, result) { + window.location = baseURL + result.url; + }); +}); diff --git a/docs/1.1.0/AsyncHTTPClient/js/jquery.min.js b/docs/1.1.0/AsyncHTTPClient/js/jquery.min.js new file mode 100644 index 000000000..a1c07fd80 --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.4.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1",k=function(e,t){return new k.fn.init(e,t)},p=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function d(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;nx",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/\s*$/g;function Oe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Vt,Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||k.expando+"_"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Yt,"$1"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||k.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Vt.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,"position"),c=k(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=k.css(e,"top"),u=k.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,k.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===k.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===k.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),i.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-k.css(r,"marginTop",!0),left:t.left-i.left-k.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===k.css(e,"position"))e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;k.fn[t]=function(e){return _(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(a,s){k.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){k.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return _(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){k.fn[n]=function(e,t){return 0=this.length)return z.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},z.QueryLexer.prototype.width=function(){return this.pos-this.start},z.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},z.QueryLexer.prototype.backup=function(){this.pos-=1},z.QueryLexer.prototype.acceptDigitRun=function(){for(var e,t;47<(t=(e=this.next()).charCodeAt(0))&&t<58;);e!=z.QueryLexer.EOS&&this.backup()},z.QueryLexer.prototype.more=function(){return this.pos', + menu: '
' + }; + } + function buildSelectors(classes) { + var selectors = {}; + _.each(classes, function(v, k) { + selectors[k] = "." + v; + }); + return selectors; + } + function buildCss() { + var css = { + wrapper: { + position: "relative", + display: "inline-block" + }, + hint: { + position: "absolute", + top: "0", + left: "0", + borderColor: "transparent", + boxShadow: "none", + opacity: "1" + }, + input: { + position: "relative", + verticalAlign: "top", + backgroundColor: "transparent" + }, + inputWithNoHint: { + position: "relative", + verticalAlign: "top" + }, + menu: { + position: "absolute", + top: "100%", + left: "0", + zIndex: "100", + display: "none" + }, + ltr: { + left: "0", + right: "auto" + }, + rtl: { + left: "auto", + right: " 0" + } + }; + if (_.isMsie()) { + _.mixin(css.input, { + backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" + }); + } + return css; + } + }(); + var EventBus = function() { + "use strict"; + var namespace, deprecationMap; + namespace = "typeahead:"; + deprecationMap = { + render: "rendered", + cursorchange: "cursorchanged", + select: "selected", + autocomplete: "autocompleted" + }; + function EventBus(o) { + if (!o || !o.el) { + $.error("EventBus initialized without el"); + } + this.$el = $(o.el); + } + _.mixin(EventBus.prototype, { + _trigger: function(type, args) { + var $e = $.Event(namespace + type); + this.$el.trigger.call(this.$el, $e, args || []); + return $e; + }, + before: function(type) { + var args, $e; + args = [].slice.call(arguments, 1); + $e = this._trigger("before" + type, args); + return $e.isDefaultPrevented(); + }, + trigger: function(type) { + var deprecatedType; + this._trigger(type, [].slice.call(arguments, 1)); + if (deprecatedType = deprecationMap[type]) { + this._trigger(deprecatedType, [].slice.call(arguments, 1)); + } + } + }); + return EventBus; + }(); + var EventEmitter = function() { + "use strict"; + var splitter = /\s+/, nextTick = getNextTick(); + return { + onSync: onSync, + onAsync: onAsync, + off: off, + trigger: trigger + }; + function on(method, types, cb, context) { + var type; + if (!cb) { + return this; + } + types = types.split(splitter); + cb = context ? bindContext(cb, context) : cb; + this._callbacks = this._callbacks || {}; + while (type = types.shift()) { + this._callbacks[type] = this._callbacks[type] || { + sync: [], + async: [] + }; + this._callbacks[type][method].push(cb); + } + return this; + } + function onAsync(types, cb, context) { + return on.call(this, "async", types, cb, context); + } + function onSync(types, cb, context) { + return on.call(this, "sync", types, cb, context); + } + function off(types) { + var type; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + while (type = types.shift()) { + delete this._callbacks[type]; + } + return this; + } + function trigger(types) { + var type, callbacks, args, syncFlush, asyncFlush; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + args = [].slice.call(arguments, 1); + while ((type = types.shift()) && (callbacks = this._callbacks[type])) { + syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args)); + asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args)); + syncFlush() && nextTick(asyncFlush); + } + return this; + } + function getFlush(callbacks, context, args) { + return flush; + function flush() { + var cancelled; + for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) { + cancelled = callbacks[i].apply(context, args) === false; + } + return !cancelled; + } + } + function getNextTick() { + var nextTickFn; + if (window.setImmediate) { + nextTickFn = function nextTickSetImmediate(fn) { + setImmediate(function() { + fn(); + }); + }; + } else { + nextTickFn = function nextTickSetTimeout(fn) { + setTimeout(function() { + fn(); + }, 0); + }; + } + return nextTickFn; + } + function bindContext(fn, context) { + return fn.bind ? fn.bind(context) : function() { + fn.apply(context, [].slice.call(arguments, 0)); + }; + } + }(); + var highlight = function(doc) { + "use strict"; + var defaults = { + node: null, + pattern: null, + tagName: "strong", + className: null, + wordsOnly: false, + caseSensitive: false, + diacriticInsensitive: false + }; + var accented = { + A: "[AaªÀ-Åà-åĀ-ąǍǎȀ-ȃȦȧᴬᵃḀḁẚẠ-ảₐ℀℁℻⒜Ⓐⓐ㍱-㍴㎀-㎄㎈㎉㎩-㎯㏂㏊㏟㏿Aa]", + B: "[BbᴮᵇḂ-ḇℬ⒝Ⓑⓑ㍴㎅-㎇㏃㏈㏔㏝Bb]", + C: "[CcÇçĆ-čᶜ℀ℂ℃℅℆ℭⅭⅽ⒞Ⓒⓒ㍶㎈㎉㎝㎠㎤㏄-㏇Cc]", + D: "[DdĎďDŽ-džDZ-dzᴰᵈḊ-ḓⅅⅆⅮⅾ⒟Ⓓⓓ㋏㍲㍷-㍹㎗㎭-㎯㏅㏈Dd]", + E: "[EeÈ-Ëè-ëĒ-ěȄ-ȇȨȩᴱᵉḘ-ḛẸ-ẽₑ℡ℯℰⅇ⒠Ⓔⓔ㉐㋍㋎Ee]", + F: "[FfᶠḞḟ℉ℱ℻⒡Ⓕⓕ㎊-㎌㎙ff-fflFf]", + G: "[GgĜ-ģǦǧǴǵᴳᵍḠḡℊ⒢Ⓖⓖ㋌㋍㎇㎍-㎏㎓㎬㏆㏉㏒㏿Gg]", + H: "[HhĤĥȞȟʰᴴḢ-ḫẖℋ-ℎ⒣Ⓗⓗ㋌㍱㎐-㎔㏊㏋㏗Hh]", + I: "[IiÌ-Ïì-ïĨ-İIJijǏǐȈ-ȋᴵᵢḬḭỈ-ịⁱℐℑℹⅈⅠ-ⅣⅥ-ⅨⅪⅫⅰ-ⅳⅵ-ⅸⅺⅻ⒤Ⓘⓘ㍺㏌㏕fiffiIi]", + J: "[JjIJ-ĵLJ-njǰʲᴶⅉ⒥ⒿⓙⱼJj]", + K: "[KkĶķǨǩᴷᵏḰ-ḵK⒦Ⓚⓚ㎄㎅㎉㎏㎑㎘㎞㎢㎦㎪㎸㎾㏀㏆㏍-㏏Kk]", + L: "[LlĹ-ŀLJ-ljˡᴸḶḷḺ-ḽℒℓ℡Ⅼⅼ⒧Ⓛⓛ㋏㎈㎉㏐-㏓㏕㏖㏿flfflLl]", + M: "[MmᴹᵐḾ-ṃ℠™ℳⅯⅿ⒨Ⓜⓜ㍷-㍹㎃㎆㎎㎒㎖㎙-㎨㎫㎳㎷㎹㎽㎿㏁㏂㏎㏐㏔-㏖㏘㏙㏞㏟Mm]", + N: "[NnÑñŃ-ʼnNJ-njǸǹᴺṄ-ṋⁿℕ№⒩Ⓝⓝ㎁㎋㎚㎱㎵㎻㏌㏑Nn]", + O: "[OoºÒ-Öò-öŌ-őƠơǑǒǪǫȌ-ȏȮȯᴼᵒỌ-ỏₒ℅№ℴ⒪Ⓞⓞ㍵㏇㏒㏖Oo]", + P: "[PpᴾᵖṔ-ṗℙ⒫Ⓟⓟ㉐㍱㍶㎀㎊㎩-㎬㎰㎴㎺㏋㏗-㏚Pp]", + Q: "[Qqℚ⒬Ⓠⓠ㏃Qq]", + R: "[RrŔ-řȐ-ȓʳᴿᵣṘ-ṛṞṟ₨ℛ-ℝ⒭Ⓡⓡ㋍㍴㎭-㎯㏚㏛Rr]", + S: "[SsŚ-šſȘșˢṠ-ṣ₨℁℠⒮Ⓢⓢ㎧㎨㎮-㎳㏛㏜stSs]", + T: "[TtŢ-ťȚțᵀᵗṪ-ṱẗ℡™⒯Ⓣⓣ㉐㋏㎔㏏ſtstTt]", + U: "[UuÙ-Üù-üŨ-ųƯưǓǔȔ-ȗᵁᵘᵤṲ-ṷỤ-ủ℆⒰Ⓤⓤ㍳㍺Uu]", + V: "[VvᵛᵥṼ-ṿⅣ-Ⅷⅳ-ⅷ⒱Ⓥⓥⱽ㋎㍵㎴-㎹㏜㏞Vv]", + W: "[WwŴŵʷᵂẀ-ẉẘ⒲Ⓦⓦ㎺-㎿㏝Ww]", + X: "[XxˣẊ-ẍₓ℻Ⅸ-Ⅻⅸ-ⅻ⒳Ⓧⓧ㏓Xx]", + Y: "[YyÝýÿŶ-ŸȲȳʸẎẏẙỲ-ỹ⒴Ⓨⓨ㏉Yy]", + Z: "[ZzŹ-žDZ-dzᶻẐ-ẕℤℨ⒵Ⓩⓩ㎐-㎔Zz]" + }; + return function hightlight(o) { + var regex; + o = _.mixin({}, defaults, o); + if (!o.node || !o.pattern) { + return; + } + o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; + regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly, o.diacriticInsensitive); + traverse(o.node, hightlightTextNode); + function hightlightTextNode(textNode) { + var match, patternNode, wrapperNode; + if (match = regex.exec(textNode.data)) { + wrapperNode = doc.createElement(o.tagName); + o.className && (wrapperNode.className = o.className); + patternNode = textNode.splitText(match.index); + patternNode.splitText(match[0].length); + wrapperNode.appendChild(patternNode.cloneNode(true)); + textNode.parentNode.replaceChild(wrapperNode, patternNode); + } + return !!match; + } + function traverse(el, hightlightTextNode) { + var childNode, TEXT_NODE_TYPE = 3; + for (var i = 0; i < el.childNodes.length; i++) { + childNode = el.childNodes[i]; + if (childNode.nodeType === TEXT_NODE_TYPE) { + i += hightlightTextNode(childNode) ? 1 : 0; + } else { + traverse(childNode, hightlightTextNode); + } + } + } + }; + function accent_replacer(chr) { + return accented[chr.toUpperCase()] || chr; + } + function getRegex(patterns, caseSensitive, wordsOnly, diacriticInsensitive) { + var escapedPatterns = [], regexStr; + for (var i = 0, len = patterns.length; i < len; i++) { + var escapedWord = _.escapeRegExChars(patterns[i]); + if (diacriticInsensitive) { + escapedWord = escapedWord.replace(/\S/g, accent_replacer); + } + escapedPatterns.push(escapedWord); + } + regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")"; + return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); + } + }(window.document); + var Input = function() { + "use strict"; + var specialKeyCodeMap; + specialKeyCodeMap = { + 9: "tab", + 27: "esc", + 37: "left", + 39: "right", + 13: "enter", + 38: "up", + 40: "down" + }; + function Input(o, www) { + o = o || {}; + if (!o.input) { + $.error("input is missing"); + } + www.mixin(this); + this.$hint = $(o.hint); + this.$input = $(o.input); + this.$input.attr({ + "aria-activedescendant": "", + "aria-owns": this.$input.attr("id") + "_listbox", + role: "combobox", + "aria-readonly": "true", + "aria-autocomplete": "list" + }); + $(www.menu).attr("id", this.$input.attr("id") + "_listbox"); + this.query = this.$input.val(); + this.queryWhenFocused = this.hasFocus() ? this.query : null; + this.$overflowHelper = buildOverflowHelper(this.$input); + this._checkLanguageDirection(); + if (this.$hint.length === 0) { + this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; + } + this.onSync("cursorchange", this._updateDescendent); + } + Input.normalizeQuery = function(str) { + return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " "); + }; + _.mixin(Input.prototype, EventEmitter, { + _onBlur: function onBlur() { + this.resetInputValue(); + this.trigger("blurred"); + }, + _onFocus: function onFocus() { + this.queryWhenFocused = this.query; + this.trigger("focused"); + }, + _onKeydown: function onKeydown($e) { + var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; + this._managePreventDefault(keyName, $e); + if (keyName && this._shouldTrigger(keyName, $e)) { + this.trigger(keyName + "Keyed", $e); + } + }, + _onInput: function onInput() { + this._setQuery(this.getInputValue()); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + _managePreventDefault: function managePreventDefault(keyName, $e) { + var preventDefault; + switch (keyName) { + case "up": + case "down": + preventDefault = !withModifier($e); + break; + + default: + preventDefault = false; + } + preventDefault && $e.preventDefault(); + }, + _shouldTrigger: function shouldTrigger(keyName, $e) { + var trigger; + switch (keyName) { + case "tab": + trigger = !withModifier($e); + break; + + default: + trigger = true; + } + return trigger; + }, + _checkLanguageDirection: function checkLanguageDirection() { + var dir = (this.$input.css("direction") || "ltr").toLowerCase(); + if (this.dir !== dir) { + this.dir = dir; + this.$hint.attr("dir", dir); + this.trigger("langDirChanged", dir); + } + }, + _setQuery: function setQuery(val, silent) { + var areEquivalent, hasDifferentWhitespace; + areEquivalent = areQueriesEquivalent(val, this.query); + hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false; + this.query = val; + if (!silent && !areEquivalent) { + this.trigger("queryChanged", this.query); + } else if (!silent && hasDifferentWhitespace) { + this.trigger("whitespaceChanged", this.query); + } + }, + _updateDescendent: function updateDescendent(event, id) { + this.$input.attr("aria-activedescendant", id); + }, + bind: function() { + var that = this, onBlur, onFocus, onKeydown, onInput; + onBlur = _.bind(this._onBlur, this); + onFocus = _.bind(this._onFocus, this); + onKeydown = _.bind(this._onKeydown, this); + onInput = _.bind(this._onInput, this); + this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown); + if (!_.isMsie() || _.isMsie() > 9) { + this.$input.on("input.tt", onInput); + } else { + this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { + if (specialKeyCodeMap[$e.which || $e.keyCode]) { + return; + } + _.defer(_.bind(that._onInput, that, $e)); + }); + } + return this; + }, + focus: function focus() { + this.$input.focus(); + }, + blur: function blur() { + this.$input.blur(); + }, + getLangDir: function getLangDir() { + return this.dir; + }, + getQuery: function getQuery() { + return this.query || ""; + }, + setQuery: function setQuery(val, silent) { + this.setInputValue(val); + this._setQuery(val, silent); + }, + hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() { + return this.query !== this.queryWhenFocused; + }, + getInputValue: function getInputValue() { + return this.$input.val(); + }, + setInputValue: function setInputValue(value) { + this.$input.val(value); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + resetInputValue: function resetInputValue() { + this.setInputValue(this.query); + }, + getHint: function getHint() { + return this.$hint.val(); + }, + setHint: function setHint(value) { + this.$hint.val(value); + }, + clearHint: function clearHint() { + this.setHint(""); + }, + clearHintIfInvalid: function clearHintIfInvalid() { + var val, hint, valIsPrefixOfHint, isValid; + val = this.getInputValue(); + hint = this.getHint(); + valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; + isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow(); + !isValid && this.clearHint(); + }, + hasFocus: function hasFocus() { + return this.$input.is(":focus"); + }, + hasOverflow: function hasOverflow() { + var constraint = this.$input.width() - 2; + this.$overflowHelper.text(this.getInputValue()); + return this.$overflowHelper.width() >= constraint; + }, + isCursorAtEnd: function() { + var valueLength, selectionStart, range; + valueLength = this.$input.val().length; + selectionStart = this.$input[0].selectionStart; + if (_.isNumber(selectionStart)) { + return selectionStart === valueLength; + } else if (document.selection) { + range = document.selection.createRange(); + range.moveStart("character", -valueLength); + return valueLength === range.text.length; + } + return true; + }, + destroy: function destroy() { + this.$hint.off(".tt"); + this.$input.off(".tt"); + this.$overflowHelper.remove(); + this.$hint = this.$input = this.$overflowHelper = $("
"); + } + }); + return Input; + function buildOverflowHelper($input) { + return $('').css({ + position: "absolute", + visibility: "hidden", + whiteSpace: "pre", + fontFamily: $input.css("font-family"), + fontSize: $input.css("font-size"), + fontStyle: $input.css("font-style"), + fontVariant: $input.css("font-variant"), + fontWeight: $input.css("font-weight"), + wordSpacing: $input.css("word-spacing"), + letterSpacing: $input.css("letter-spacing"), + textIndent: $input.css("text-indent"), + textRendering: $input.css("text-rendering"), + textTransform: $input.css("text-transform") + }).insertAfter($input); + } + function areQueriesEquivalent(a, b) { + return Input.normalizeQuery(a) === Input.normalizeQuery(b); + } + function withModifier($e) { + return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; + } + }(); + var Dataset = function() { + "use strict"; + var keys, nameGenerator; + keys = { + dataset: "tt-selectable-dataset", + val: "tt-selectable-display", + obj: "tt-selectable-object" + }; + nameGenerator = _.getIdGenerator(); + function Dataset(o, www) { + o = o || {}; + o.templates = o.templates || {}; + o.templates.notFound = o.templates.notFound || o.templates.empty; + if (!o.source) { + $.error("missing source"); + } + if (!o.node) { + $.error("missing node"); + } + if (o.name && !isValidName(o.name)) { + $.error("invalid dataset name: " + o.name); + } + www.mixin(this); + this.highlight = !!o.highlight; + this.name = _.toStr(o.name || nameGenerator()); + this.limit = o.limit || 5; + this.displayFn = getDisplayFn(o.display || o.displayKey); + this.templates = getTemplates(o.templates, this.displayFn); + this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source; + this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async; + this._resetLastSuggestion(); + this.$el = $(o.node).attr("role", "presentation").addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); + } + Dataset.extractData = function extractData(el) { + var $el = $(el); + if ($el.data(keys.obj)) { + return { + dataset: $el.data(keys.dataset) || "", + val: $el.data(keys.val) || "", + obj: $el.data(keys.obj) || null + }; + } + return null; + }; + _.mixin(Dataset.prototype, EventEmitter, { + _overwrite: function overwrite(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (this.async && this.templates.pending) { + this._renderPending(query); + } else if (!this.async && this.templates.notFound) { + this._renderNotFound(query); + } else { + this._empty(); + } + this.trigger("rendered", suggestions, false, this.name); + }, + _append: function append(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length && this.$lastSuggestion.length) { + this._appendSuggestions(query, suggestions); + } else if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (!this.$lastSuggestion.length && this.templates.notFound) { + this._renderNotFound(query); + } + this.trigger("rendered", suggestions, true, this.name); + }, + _renderSuggestions: function renderSuggestions(query, suggestions) { + var $fragment; + $fragment = this._getSuggestionsFragment(query, suggestions); + this.$lastSuggestion = $fragment.children().last(); + this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions)); + }, + _appendSuggestions: function appendSuggestions(query, suggestions) { + var $fragment, $lastSuggestion; + $fragment = this._getSuggestionsFragment(query, suggestions); + $lastSuggestion = $fragment.children().last(); + this.$lastSuggestion.after($fragment); + this.$lastSuggestion = $lastSuggestion; + }, + _renderPending: function renderPending(query) { + var template = this.templates.pending; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _renderNotFound: function renderNotFound(query) { + var template = this.templates.notFound; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _empty: function empty() { + this.$el.empty(); + this._resetLastSuggestion(); + }, + _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) { + var that = this, fragment; + fragment = document.createDocumentFragment(); + _.each(suggestions, function getSuggestionNode(suggestion) { + var $el, context; + context = that._injectQuery(query, suggestion); + $el = $(that.templates.suggestion(context)).data(keys.dataset, that.name).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable); + fragment.appendChild($el[0]); + }); + this.highlight && highlight({ + className: this.classes.highlight, + node: fragment, + pattern: query + }); + return $(fragment); + }, + _getFooter: function getFooter(query, suggestions) { + return this.templates.footer ? this.templates.footer({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _getHeader: function getHeader(query, suggestions) { + return this.templates.header ? this.templates.header({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _resetLastSuggestion: function resetLastSuggestion() { + this.$lastSuggestion = $(); + }, + _injectQuery: function injectQuery(query, obj) { + return _.isObject(obj) ? _.mixin({ + _query: query + }, obj) : obj; + }, + update: function update(query) { + var that = this, canceled = false, syncCalled = false, rendered = 0; + this.cancel(); + this.cancel = function cancel() { + canceled = true; + that.cancel = $.noop; + that.async && that.trigger("asyncCanceled", query, that.name); + }; + this.source(query, sync, async); + !syncCalled && sync([]); + function sync(suggestions) { + if (syncCalled) { + return; + } + syncCalled = true; + suggestions = (suggestions || []).slice(0, that.limit); + rendered = suggestions.length; + that._overwrite(query, suggestions); + if (rendered < that.limit && that.async) { + that.trigger("asyncRequested", query, that.name); + } + } + function async(suggestions) { + suggestions = suggestions || []; + if (!canceled && rendered < that.limit) { + that.cancel = $.noop; + var idx = Math.abs(rendered - that.limit); + rendered += idx; + that._append(query, suggestions.slice(0, idx)); + that.async && that.trigger("asyncReceived", query, that.name); + } + } + }, + cancel: $.noop, + clear: function clear() { + this._empty(); + this.cancel(); + this.trigger("cleared"); + }, + isEmpty: function isEmpty() { + return this.$el.is(":empty"); + }, + destroy: function destroy() { + this.$el = $("
"); + } + }); + return Dataset; + function getDisplayFn(display) { + display = display || _.stringify; + return _.isFunction(display) ? display : displayFn; + function displayFn(obj) { + return obj[display]; + } + } + function getTemplates(templates, displayFn) { + return { + notFound: templates.notFound && _.templatify(templates.notFound), + pending: templates.pending && _.templatify(templates.pending), + header: templates.header && _.templatify(templates.header), + footer: templates.footer && _.templatify(templates.footer), + suggestion: templates.suggestion || suggestionTemplate + }; + function suggestionTemplate(context) { + return $('
').attr("id", _.guid()).text(displayFn(context)); + } + } + function isValidName(str) { + return /^[_a-zA-Z0-9-]+$/.test(str); + } + }(); + var Menu = function() { + "use strict"; + function Menu(o, www) { + var that = this; + o = o || {}; + if (!o.node) { + $.error("node is required"); + } + www.mixin(this); + this.$node = $(o.node); + this.query = null; + this.datasets = _.map(o.datasets, initializeDataset); + function initializeDataset(oDataset) { + var node = that.$node.find(oDataset.node).first(); + oDataset.node = node.length ? node : $("
").appendTo(that.$node); + return new Dataset(oDataset, www); + } + } + _.mixin(Menu.prototype, EventEmitter, { + _onSelectableClick: function onSelectableClick($e) { + this.trigger("selectableClicked", $($e.currentTarget)); + }, + _onRendered: function onRendered(type, dataset, suggestions, async) { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetRendered", dataset, suggestions, async); + }, + _onCleared: function onCleared() { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetCleared"); + }, + _propagate: function propagate() { + this.trigger.apply(this, arguments); + }, + _allDatasetsEmpty: function allDatasetsEmpty() { + return _.every(this.datasets, _.bind(function isDatasetEmpty(dataset) { + var isEmpty = dataset.isEmpty(); + this.$node.attr("aria-expanded", !isEmpty); + return isEmpty; + }, this)); + }, + _getSelectables: function getSelectables() { + return this.$node.find(this.selectors.selectable); + }, + _removeCursor: function _removeCursor() { + var $selectable = this.getActiveSelectable(); + $selectable && $selectable.removeClass(this.classes.cursor); + }, + _ensureVisible: function ensureVisible($el) { + var elTop, elBottom, nodeScrollTop, nodeHeight; + elTop = $el.position().top; + elBottom = elTop + $el.outerHeight(true); + nodeScrollTop = this.$node.scrollTop(); + nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10); + if (elTop < 0) { + this.$node.scrollTop(nodeScrollTop + elTop); + } else if (nodeHeight < elBottom) { + this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight)); + } + }, + bind: function() { + var that = this, onSelectableClick; + onSelectableClick = _.bind(this._onSelectableClick, this); + this.$node.on("click.tt", this.selectors.selectable, onSelectableClick); + this.$node.on("mouseover", this.selectors.selectable, function() { + that.setCursor($(this)); + }); + this.$node.on("mouseleave", function() { + that._removeCursor(); + }); + _.each(this.datasets, function(dataset) { + dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that); + }); + return this; + }, + isOpen: function isOpen() { + return this.$node.hasClass(this.classes.open); + }, + open: function open() { + this.$node.scrollTop(0); + this.$node.addClass(this.classes.open); + }, + close: function close() { + this.$node.attr("aria-expanded", false); + this.$node.removeClass(this.classes.open); + this._removeCursor(); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.attr("dir", dir); + }, + selectableRelativeToCursor: function selectableRelativeToCursor(delta) { + var $selectables, $oldCursor, oldIndex, newIndex; + $oldCursor = this.getActiveSelectable(); + $selectables = this._getSelectables(); + oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1; + newIndex = oldIndex + delta; + newIndex = (newIndex + 1) % ($selectables.length + 1) - 1; + newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex; + return newIndex === -1 ? null : $selectables.eq(newIndex); + }, + setCursor: function setCursor($selectable) { + this._removeCursor(); + if ($selectable = $selectable && $selectable.first()) { + $selectable.addClass(this.classes.cursor); + this._ensureVisible($selectable); + } + }, + getSelectableData: function getSelectableData($el) { + return $el && $el.length ? Dataset.extractData($el) : null; + }, + getActiveSelectable: function getActiveSelectable() { + var $selectable = this._getSelectables().filter(this.selectors.cursor).first(); + return $selectable.length ? $selectable : null; + }, + getTopSelectable: function getTopSelectable() { + var $selectable = this._getSelectables().first(); + return $selectable.length ? $selectable : null; + }, + update: function update(query) { + var isValidUpdate = query !== this.query; + if (isValidUpdate) { + this.query = query; + _.each(this.datasets, updateDataset); + } + return isValidUpdate; + function updateDataset(dataset) { + dataset.update(query); + } + }, + empty: function empty() { + _.each(this.datasets, clearDataset); + this.query = null; + this.$node.addClass(this.classes.empty); + function clearDataset(dataset) { + dataset.clear(); + } + }, + destroy: function destroy() { + this.$node.off(".tt"); + this.$node = $("
"); + _.each(this.datasets, destroyDataset); + function destroyDataset(dataset) { + dataset.destroy(); + } + } + }); + return Menu; + }(); + var Status = function() { + "use strict"; + function Status(options) { + this.$el = $("", { + role: "status", + "aria-live": "polite" + }).css({ + position: "absolute", + padding: "0", + border: "0", + height: "1px", + width: "1px", + "margin-bottom": "-1px", + "margin-right": "-1px", + overflow: "hidden", + clip: "rect(0 0 0 0)", + "white-space": "nowrap" + }); + options.$input.after(this.$el); + _.each(options.menu.datasets, _.bind(function(dataset) { + if (dataset.onSync) { + dataset.onSync("rendered", _.bind(this.update, this)); + dataset.onSync("cleared", _.bind(this.cleared, this)); + } + }, this)); + } + _.mixin(Status.prototype, { + update: function update(event, suggestions) { + var length = suggestions.length; + var words; + if (length === 1) { + words = { + result: "result", + is: "is" + }; + } else { + words = { + result: "results", + is: "are" + }; + } + this.$el.text(length + " " + words.result + " " + words.is + " available, use up and down arrow keys to navigate."); + }, + cleared: function() { + this.$el.text(""); + } + }); + return Status; + }(); + var DefaultMenu = function() { + "use strict"; + var s = Menu.prototype; + function DefaultMenu() { + Menu.apply(this, [].slice.call(arguments, 0)); + } + _.mixin(DefaultMenu.prototype, Menu.prototype, { + open: function open() { + !this._allDatasetsEmpty() && this._show(); + return s.open.apply(this, [].slice.call(arguments, 0)); + }, + close: function close() { + this._hide(); + return s.close.apply(this, [].slice.call(arguments, 0)); + }, + _onRendered: function onRendered() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onRendered.apply(this, [].slice.call(arguments, 0)); + }, + _onCleared: function onCleared() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onCleared.apply(this, [].slice.call(arguments, 0)); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl); + return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0)); + }, + _hide: function hide() { + this.$node.hide(); + }, + _show: function show() { + this.$node.css("display", "block"); + } + }); + return DefaultMenu; + }(); + var Typeahead = function() { + "use strict"; + function Typeahead(o, www) { + var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged; + o = o || {}; + if (!o.input) { + $.error("missing input"); + } + if (!o.menu) { + $.error("missing menu"); + } + if (!o.eventBus) { + $.error("missing event bus"); + } + www.mixin(this); + this.eventBus = o.eventBus; + this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; + this.input = o.input; + this.menu = o.menu; + this.enabled = true; + this.autoselect = !!o.autoselect; + this.active = false; + this.input.hasFocus() && this.activate(); + this.dir = this.input.getLangDir(); + this._hacks(); + this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this); + onFocused = c(this, "activate", "open", "_onFocused"); + onBlurred = c(this, "deactivate", "_onBlurred"); + onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed"); + onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed"); + onEscKeyed = c(this, "isActive", "_onEscKeyed"); + onUpKeyed = c(this, "isActive", "open", "_onUpKeyed"); + onDownKeyed = c(this, "isActive", "open", "_onDownKeyed"); + onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed"); + onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed"); + onQueryChanged = c(this, "_openIfActive", "_onQueryChanged"); + onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged"); + this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this); + } + _.mixin(Typeahead.prototype, { + _hacks: function hacks() { + var $input, $menu; + $input = this.input.$input || $("
"); + $menu = this.menu.$node || $("
"); + $input.on("blur.tt", function($e) { + var active, isActive, hasActive; + active = document.activeElement; + isActive = $menu.is(active); + hasActive = $menu.has(active).length > 0; + if (_.isMsie() && (isActive || hasActive)) { + $e.preventDefault(); + $e.stopImmediatePropagation(); + _.defer(function() { + $input.focus(); + }); + } + }); + $menu.on("mousedown.tt", function($e) { + $e.preventDefault(); + }); + }, + _onSelectableClicked: function onSelectableClicked(type, $el) { + this.select($el); + }, + _onDatasetCleared: function onDatasetCleared() { + this._updateHint(); + }, + _onDatasetRendered: function onDatasetRendered(type, suggestions, async, dataset) { + this._updateHint(); + if (this.autoselect) { + var cursorClass = this.selectors.cursor.substr(1); + this.menu.$node.find(this.selectors.suggestion).first().addClass(cursorClass); + } + this.eventBus.trigger("render", suggestions, async, dataset); + }, + _onAsyncRequested: function onAsyncRequested(type, dataset, query) { + this.eventBus.trigger("asyncrequest", query, dataset); + }, + _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) { + this.eventBus.trigger("asynccancel", query, dataset); + }, + _onAsyncReceived: function onAsyncReceived(type, dataset, query) { + this.eventBus.trigger("asyncreceive", query, dataset); + }, + _onFocused: function onFocused() { + this._minLengthMet() && this.menu.update(this.input.getQuery()); + }, + _onBlurred: function onBlurred() { + if (this.input.hasQueryChangedSinceLastFocus()) { + this.eventBus.trigger("change", this.input.getQuery()); + } + }, + _onEnterKeyed: function onEnterKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + if (this.select($selectable)) { + $e.preventDefault(); + $e.stopPropagation(); + } + } else if (this.autoselect) { + if (this.select(this.menu.getTopSelectable())) { + $e.preventDefault(); + $e.stopPropagation(); + } + } + }, + _onTabKeyed: function onTabKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + this.select($selectable) && $e.preventDefault(); + } else if ($selectable = this.menu.getTopSelectable()) { + this.autocomplete($selectable) && $e.preventDefault(); + } + }, + _onEscKeyed: function onEscKeyed() { + this.close(); + }, + _onUpKeyed: function onUpKeyed() { + this.moveCursor(-1); + }, + _onDownKeyed: function onDownKeyed() { + this.moveCursor(+1); + }, + _onLeftKeyed: function onLeftKeyed() { + if (this.dir === "rtl" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); + } + }, + _onRightKeyed: function onRightKeyed() { + if (this.dir === "ltr" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); + } + }, + _onQueryChanged: function onQueryChanged(e, query) { + this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty(); + }, + _onWhitespaceChanged: function onWhitespaceChanged() { + this._updateHint(); + }, + _onLangDirChanged: function onLangDirChanged(e, dir) { + if (this.dir !== dir) { + this.dir = dir; + this.menu.setLanguageDirection(dir); + } + }, + _openIfActive: function openIfActive() { + this.isActive() && this.open(); + }, + _minLengthMet: function minLengthMet(query) { + query = _.isString(query) ? query : this.input.getQuery() || ""; + return query.length >= this.minLength; + }, + _updateHint: function updateHint() { + var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match; + $selectable = this.menu.getTopSelectable(); + data = this.menu.getSelectableData($selectable); + val = this.input.getInputValue(); + if (data && !_.isBlankString(val) && !this.input.hasOverflow()) { + query = Input.normalizeQuery(val); + escapedQuery = _.escapeRegExChars(query); + frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i"); + match = frontMatchRegEx.exec(data.val); + match && this.input.setHint(val + match[1]); + } else { + this.input.clearHint(); + } + }, + isEnabled: function isEnabled() { + return this.enabled; + }, + enable: function enable() { + this.enabled = true; + }, + disable: function disable() { + this.enabled = false; + }, + isActive: function isActive() { + return this.active; + }, + activate: function activate() { + if (this.isActive()) { + return true; + } else if (!this.isEnabled() || this.eventBus.before("active")) { + return false; + } else { + this.active = true; + this.eventBus.trigger("active"); + return true; + } + }, + deactivate: function deactivate() { + if (!this.isActive()) { + return true; + } else if (this.eventBus.before("idle")) { + return false; + } else { + this.active = false; + this.close(); + this.eventBus.trigger("idle"); + return true; + } + }, + isOpen: function isOpen() { + return this.menu.isOpen(); + }, + open: function open() { + if (!this.isOpen() && !this.eventBus.before("open")) { + this.menu.open(); + this._updateHint(); + this.eventBus.trigger("open"); + } + return this.isOpen(); + }, + close: function close() { + if (this.isOpen() && !this.eventBus.before("close")) { + this.menu.close(); + this.input.clearHint(); + this.input.resetInputValue(); + this.eventBus.trigger("close"); + } + return !this.isOpen(); + }, + setVal: function setVal(val) { + this.input.setQuery(_.toStr(val)); + }, + getVal: function getVal() { + return this.input.getQuery(); + }, + select: function select($selectable) { + var data = this.menu.getSelectableData($selectable); + if (data && !this.eventBus.before("select", data.obj, data.dataset)) { + this.input.setQuery(data.val, true); + this.eventBus.trigger("select", data.obj, data.dataset); + this.close(); + return true; + } + return false; + }, + autocomplete: function autocomplete($selectable) { + var query, data, isValid; + query = this.input.getQuery(); + data = this.menu.getSelectableData($selectable); + isValid = data && query !== data.val; + if (isValid && !this.eventBus.before("autocomplete", data.obj, data.dataset)) { + this.input.setQuery(data.val); + this.eventBus.trigger("autocomplete", data.obj, data.dataset); + return true; + } + return false; + }, + moveCursor: function moveCursor(delta) { + var query, $candidate, data, suggestion, datasetName, cancelMove, id; + query = this.input.getQuery(); + $candidate = this.menu.selectableRelativeToCursor(delta); + data = this.menu.getSelectableData($candidate); + suggestion = data ? data.obj : null; + datasetName = data ? data.dataset : null; + id = $candidate ? $candidate.attr("id") : null; + this.input.trigger("cursorchange", id); + cancelMove = this._minLengthMet() && this.menu.update(query); + if (!cancelMove && !this.eventBus.before("cursorchange", suggestion, datasetName)) { + this.menu.setCursor($candidate); + if (data) { + this.input.setInputValue(data.val); + } else { + this.input.resetInputValue(); + this._updateHint(); + } + this.eventBus.trigger("cursorchange", suggestion, datasetName); + return true; + } + return false; + }, + destroy: function destroy() { + this.input.destroy(); + this.menu.destroy(); + } + }); + return Typeahead; + function c(ctx) { + var methods = [].slice.call(arguments, 1); + return function() { + var args = [].slice.call(arguments); + _.each(methods, function(method) { + return ctx[method].apply(ctx, args); + }); + }; + } + }(); + (function() { + "use strict"; + var old, keys, methods; + old = $.fn.typeahead; + keys = { + www: "tt-www", + attrs: "tt-attrs", + typeahead: "tt-typeahead" + }; + methods = { + initialize: function initialize(o, datasets) { + var www; + datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); + o = o || {}; + www = WWW(o.classNames); + return this.each(attach); + function attach() { + var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, status, typeahead, MenuConstructor; + _.each(datasets, function(d) { + d.highlight = !!o.highlight; + }); + $input = $(this); + $wrapper = $(www.html.wrapper); + $hint = $elOrNull(o.hint); + $menu = $elOrNull(o.menu); + defaultHint = o.hint !== false && !$hint; + defaultMenu = o.menu !== false && !$menu; + defaultHint && ($hint = buildHintFromInput($input, www)); + defaultMenu && ($menu = $(www.html.menu).css(www.css.menu)); + $hint && $hint.val(""); + $input = prepInput($input, www); + if (defaultHint || defaultMenu) { + $wrapper.css(www.css.wrapper); + $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint); + $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null); + } + MenuConstructor = defaultMenu ? DefaultMenu : Menu; + eventBus = new EventBus({ + el: $input + }); + input = new Input({ + hint: $hint, + input: $input + }, www); + menu = new MenuConstructor({ + node: $menu, + datasets: datasets + }, www); + status = new Status({ + $input: $input, + menu: menu + }); + typeahead = new Typeahead({ + input: input, + menu: menu, + eventBus: eventBus, + minLength: o.minLength, + autoselect: o.autoselect + }, www); + $input.data(keys.www, www); + $input.data(keys.typeahead, typeahead); + } + }, + isEnabled: function isEnabled() { + var enabled; + ttEach(this.first(), function(t) { + enabled = t.isEnabled(); + }); + return enabled; + }, + enable: function enable() { + ttEach(this, function(t) { + t.enable(); + }); + return this; + }, + disable: function disable() { + ttEach(this, function(t) { + t.disable(); + }); + return this; + }, + isActive: function isActive() { + var active; + ttEach(this.first(), function(t) { + active = t.isActive(); + }); + return active; + }, + activate: function activate() { + ttEach(this, function(t) { + t.activate(); + }); + return this; + }, + deactivate: function deactivate() { + ttEach(this, function(t) { + t.deactivate(); + }); + return this; + }, + isOpen: function isOpen() { + var open; + ttEach(this.first(), function(t) { + open = t.isOpen(); + }); + return open; + }, + open: function open() { + ttEach(this, function(t) { + t.open(); + }); + return this; + }, + close: function close() { + ttEach(this, function(t) { + t.close(); + }); + return this; + }, + select: function select(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.select($el); + }); + return success; + }, + autocomplete: function autocomplete(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.autocomplete($el); + }); + return success; + }, + moveCursor: function moveCursoe(delta) { + var success = false; + ttEach(this.first(), function(t) { + success = t.moveCursor(delta); + }); + return success; + }, + val: function val(newVal) { + var query; + if (!arguments.length) { + ttEach(this.first(), function(t) { + query = t.getVal(); + }); + return query; + } else { + ttEach(this, function(t) { + t.setVal(_.toStr(newVal)); + }); + return this; + } + }, + destroy: function destroy() { + ttEach(this, function(typeahead, $input) { + revert($input); + typeahead.destroy(); + }); + return this; + } + }; + $.fn.typeahead = function(method) { + if (methods[method]) { + return methods[method].apply(this, [].slice.call(arguments, 1)); + } else { + return methods.initialize.apply(this, arguments); + } + }; + $.fn.typeahead.noConflict = function noConflict() { + $.fn.typeahead = old; + return this; + }; + function ttEach($els, fn) { + $els.each(function() { + var $input = $(this), typeahead; + (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input); + }); + } + function buildHintFromInput($input, www) { + return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop({ + readonly: true, + required: false + }).removeAttr("id name placeholder").removeClass("required").attr({ + spellcheck: "false", + tabindex: -1 + }); + } + function prepInput($input, www) { + $input.data(keys.attrs, { + dir: $input.attr("dir"), + autocomplete: $input.attr("autocomplete"), + spellcheck: $input.attr("spellcheck"), + style: $input.attr("style") + }); + $input.addClass(www.classes.input).attr({ + spellcheck: false + }); + try { + !$input.attr("dir") && $input.attr("dir", "auto"); + } catch (e) {} + return $input; + } + function getBackgroundStyles($el) { + return { + backgroundAttachment: $el.css("background-attachment"), + backgroundClip: $el.css("background-clip"), + backgroundColor: $el.css("background-color"), + backgroundImage: $el.css("background-image"), + backgroundOrigin: $el.css("background-origin"), + backgroundPosition: $el.css("background-position"), + backgroundRepeat: $el.css("background-repeat"), + backgroundSize: $el.css("background-size") + }; + } + function revert($input) { + var www, $wrapper; + www = $input.data(keys.www); + $wrapper = $input.parent().filter(www.selectors.wrapper); + _.each($input.data(keys.attrs), function(val, key) { + _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); + }); + $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input); + if ($wrapper.length) { + $input.detach().insertAfter($wrapper); + $wrapper.remove(); + } + } + function $elOrNull(obj) { + var isValid, $el; + isValid = _.isJQuery(obj) || _.isElement(obj); + $el = isValid ? $(obj).first() : []; + return $el.length ? $el : null; + } + })(); +}); \ No newline at end of file diff --git a/docs/1.1.0/AsyncHTTPClient/search.json b/docs/1.1.0/AsyncHTTPClient/search.json new file mode 100644 index 000000000..a3986cf14 --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/search.json @@ -0,0 +1 @@ +{"Structs/HTTPClientError.html#/s:s23CustomStringConvertibleP11descriptionSSvp":{"name":"description","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV10invalidURLACvpZ":{"name":"invalidURL","abstract":"

URL provided is invalid.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV9emptyHostACvpZ":{"name":"emptyHost","abstract":"

URL does not contain host.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV15alreadyShutdownACvpZ":{"name":"alreadyShutdown","abstract":"

Client is shutdown and cannot be used for new requests.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV11emptySchemeACvpZ":{"name":"emptyScheme","abstract":"

URL does not contain scheme.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV17unsupportedSchemeyACSSFZ":{"name":"unsupportedScheme(_:)","abstract":"

Provided URL scheme is not supported, supported schemes are: http and https

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV11readTimeoutACvpZ":{"name":"readTimeout","abstract":"

Request timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV22remoteConnectionClosedACvpZ":{"name":"remoteConnectionClosed","abstract":"

Remote connection was closed unexpectedly.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV9cancelledACvpZ":{"name":"cancelled","abstract":"

Request was cancelled.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV32identityCodingIncorrectlyPresentACvpZ":{"name":"identityCodingIncorrectlyPresent","abstract":"

Request contains invalid identity encoding.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV29chunkedSpecifiedMultipleTimesACvpZ":{"name":"chunkedSpecifiedMultipleTimes","abstract":"

Request contains multiple chunks definitions.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20invalidProxyResponseACvpZ":{"name":"invalidProxyResponse","abstract":"

Proxy response was invalid.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20contentLengthMissingACvpZ":{"name":"contentLengthMissing","abstract":"

Request does not contain Content-Length header.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV27proxyAuthenticationRequiredACvpZ":{"name":"proxyAuthenticationRequired","abstract":"

Proxy Authentication Required.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20redirectLimitReachedACvpZ":{"name":"redirectLimitReached","abstract":"

Redirect Limit reached.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV21redirectCycleDetectedACvpZ":{"name":"redirectCycleDetected","abstract":"

Redirect Cycle detected.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV15uncleanShutdownACvpZ":{"name":"uncleanShutdown","abstract":"

Unclean shutdown

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html":{"name":"HTTPClientError","abstract":"

Possible client errors.

"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP0C0Qa":{"name":"Response","abstract":"

Undocumented

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didSendRequestHead4task_yAA0B0C4TaskCy_0C0QzG_8NIOHTTP1011HTTPRequestH0VtF":{"name":"didSendRequestHead(task:_:)","abstract":"

Called when the request head is sent. Will be called once.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didSendRequestPart4task_yAA0B0C4TaskCy_0C0QzG_3NIO6IODataOtF":{"name":"didSendRequestPart(task:_:)","abstract":"

Called when a part of the request body is sent. Could be called zero or more times.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didSendRequest4taskyAA0B0C4TaskCy_0C0QzG_tF":{"name":"didSendRequest(task:)","abstract":"

Called when the request is fully sent. Will be called once.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_3NIO15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","abstract":"

Called when response head is received. Will be called once.","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_3NIO15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","abstract":"

Called when part of a response body is received. Could be called zero or more times.","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","abstract":"

Called when error was thrown during request execution. Will be called zero or one time only. Request processing will be stopped after that.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","abstract":"

Called when the complete HTTP request is finished. You must return an instance of your Response associated type. Will be called once, except if an error occurred.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html":{"name":"HTTPClientResponseDelegate","abstract":"

HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed."},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B15CopyingDelegateC8Responsea":{"name":"Response","abstract":"

Undocumented

","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B15CopyingDelegateC12chunkHandlerAC3NIO15EventLoopFutureCyytGAE10ByteBufferVc_tcfc":{"name":"init(chunkHandler:)","abstract":"

Undocumented

","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_3NIO15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"HTTPClientCopyingDelegate"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient19ResponseAccumulatorC0C0a":{"name":"Response","abstract":"

Undocumented

","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient19ResponseAccumulatorC7requestAcA0B0C7RequestV_tcfc":{"name":"init(request:)","abstract":"

Undocumented

","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_3NIO15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_3NIO15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"ResponseAccumulator"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC9eventLoop3NIO05EventE0_pvp":{"name":"eventLoop","abstract":"

The EventLoop the delegate will be executed on.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC12futureResult3NIO15EventLoopFutureCyxGvp":{"name":"futureResult","abstract":"

EventLoopFuture for the response returned by this request.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC4waitxyKF":{"name":"wait()","abstract":"

Waits for execution of this request to complete.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC6cancelyyF":{"name":"cancel()","abstract":"

Cancels the request execution.

","parent_name":"Task"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV5basic8username8passwordAESS_SStFZ":{"name":"basic(username:password:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV5basic11credentialsAESS_tFZ":{"name":"basic(credentials:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV6bearer6tokensAESS_tFZ":{"name":"bearer(tokens:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV11headerValueSSvp":{"name":"headerValue","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6method8NIOHTTP110HTTPMethodOvp":{"name":"method","abstract":"

Request HTTP method, defaults to GET.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url10Foundation3URLVvp":{"name":"url","abstract":"

Remote URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6schemeSSvp":{"name":"scheme","abstract":"

Remote HTTP scheme, resolved from URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4hostSSvp":{"name":"host","abstract":"

Remote host, resolved from URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV7headers8NIOHTTP111HTTPHeadersVvp":{"name":"headers","abstract":"

Request custom HTTP Headers, defaults to no headers.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4bodyAC4BodyVSgvp":{"name":"body","abstract":"

Request body, defaults to no body.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4bodyAESS_8NIOHTTP110HTTPMethodOAJ11HTTPHeadersVAC4BodyVSgtKcfc":{"name":"init(url:method:headers:body:)","abstract":"

Create HTTP request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4bodyAE10Foundation3URLV_8NIOHTTP110HTTPMethodOAM11HTTPHeadersVAC4BodyVSgtKcfc":{"name":"init(url:method:headers:body:)","abstract":"

Create an HTTP Request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6useTLSSbvp":{"name":"useTLS","abstract":"

Whether request will be executed using secure socket.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4portSivp":{"name":"port","abstract":"

Resolved port.

","parent_name":"Request"},"Classes/HTTPClient/Body/StreamWriter.html#/s:15AsyncHTTPClient0B0C4BodyV12StreamWriterV7closureAG3NIO15EventLoopFutureCyytGAI6IODataOc_tcfc":{"name":"init(closure:)","abstract":"

Create new StreamWriter

","parent_name":"StreamWriter"},"Classes/HTTPClient/Body/StreamWriter.html#/s:15AsyncHTTPClient0B0C4BodyV12StreamWriterV5writey3NIO15EventLoopFutureCyytGAI6IODataOF":{"name":"write(_:)","abstract":"

Write data to server.

","parent_name":"StreamWriter"},"Classes/HTTPClient/Body/StreamWriter.html":{"name":"StreamWriter","abstract":"

Chunk provider.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6lengthSiSgvp":{"name":"length","abstract":"

Body size. Request validation will be failed with HTTPClientErrors.contentLengthMissing if nil,","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6streamy3NIO15EventLoopFutureCyytGAE12StreamWriterVcvp":{"name":"stream","abstract":"

Body chunk provider.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV10byteBufferyAE3NIO04ByteE0VFZ":{"name":"byteBuffer(_:)","abstract":"

Create and stream body using ByteBuffer.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6stream6length_AESiSg_3NIO15EventLoopFutureCyytGAE12StreamWriterVctFZ":{"name":"stream(length:_:)","abstract":"

Create and stream body using StreamWriter.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV4datayAE10Foundation4DataVFZ":{"name":"data(_:)","abstract":"

Create and stream body using Data.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6stringyAESSFZ":{"name":"string(_:)","abstract":"

Create and stream body using String.

","parent_name":"Body"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4hostSSvp":{"name":"host","abstract":"

Remote host of the request.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV6status8NIOHTTP118HTTPResponseStatusOvp":{"name":"status","abstract":"

Response HTTP status.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7headers8NIOHTTP111HTTPHeadersVvp":{"name":"headers","abstract":"

Reponse HTTP headers.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4body3NIO10ByteBufferVSgvp":{"name":"body","abstract":"

Response body.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4host6status7headers4bodyAESS_8NIOHTTP118HTTPResponseStatusOAJ11HTTPHeadersV3NIO10ByteBufferVSgtcfc":{"name":"init(host:status:headers:body:)","abstract":"

Create HTTP Response.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7cookiesSayAC6CookieVGvp":{"name":"cookies","abstract":"

List of HTTP cookies returned by the server.

","parent_name":"Response"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4nameSSvp":{"name":"name","abstract":"

The name of the cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV5valueSSvp":{"name":"value","abstract":"

The cookie’s string value.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4pathSSvp":{"name":"path","abstract":"

The cookie’s path.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6domainSSSgvp":{"name":"domain","abstract":"

The domain of the cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV7expires10Foundation4DateVSgvp":{"name":"expires","abstract":"

The cookie’s expiration date.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6maxAgeSiSgvp":{"name":"maxAge","abstract":"

The cookie’s age in seconds.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV8httpOnlySbvp":{"name":"httpOnly","abstract":"

Whether the cookie should only be sent to HTTP servers.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6secureSbvp":{"name":"secure","abstract":"

Whether the cookie should only be sent over secure channels.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6header13defaultDomainAESgSS_SStcfc":{"name":"init(header:defaultDomain:)","abstract":"

Create a Cookie by parsing a Set-Cookie header.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4name5value4path6domain7expires6maxAge8httpOnly6secureAESS_S3SSg10Foundation4DateVSgSiSgS2btcfc":{"name":"init(name:value:path:domain:expires:maxAge:httpOnly:secure:)","abstract":"

Create HTTP cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Decompression.html#/s:15AsyncHTTPClient0B0C13DecompressionO8disabledyA2EmF":{"name":"disabled","abstract":"

Decompression is disabled.

","parent_name":"Decompression"},"Classes/HTTPClient/Decompression.html#/s:15AsyncHTTPClient0B0C13DecompressionO7enabledyAE18NIOHTTPCompression20NIOHTTPDecompressionO0C5LimitV_tcAEmF":{"name":"enabled(limit:)","abstract":"

Decompression is enabled.

","parent_name":"Decompression"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV11indifferentAEvpZ":{"name":"indifferent","abstract":"

Event Loop will be selected by the library.

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV8delegate2onAE3NIO0cD0_p_tFZ":{"name":"delegate(on:)","abstract":"

The delegate will be run on the specified EventLoop (and the Channel if possible).

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV18delegateAndChannel2onAE3NIO0cD0_p_tFZ":{"name":"delegateAndChannel(on:)","abstract":"

The delegate and the Channel will be run on the specified EventLoop.

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopGroupProvider.html#/s:15AsyncHTTPClient0B0C22EventLoopGroupProviderO6sharedyAE3NIO0cdE0_pcAEmF":{"name":"shared(_:)","abstract":"

EventLoopGroup will be provided by the user. Owner of this group is responsible for its lifecycle.

","parent_name":"EventLoopGroupProvider"},"Classes/HTTPClient/EventLoopGroupProvider.html#/s:15AsyncHTTPClient0B0C22EventLoopGroupProviderO9createNewyA2EmF":{"name":"createNew","abstract":"

EventLoopGroup will be created by the client. When syncShutdown is called, created EventLoopGroup will be shut down as well.

","parent_name":"EventLoopGroupProvider"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV4hostSSvp":{"name":"host","abstract":"

Specifies Proxy server host.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV4portSivp":{"name":"port","abstract":"

Specifies Proxy server port.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV13authorizationAC13AuthorizationVSgvp":{"name":"authorization","abstract":"

Specifies Proxy server authorization.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV6server4host4portAGSS_SitFZ":{"name":"server(host:port:)","abstract":"

Create proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV6server4host4port13authorizationAGSS_SiAC13AuthorizationVSgtFZ":{"name":"server(host:port:authorization:)","abstract":"

Create proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08RedirectC0V8disallowAGvpZ":{"name":"disallow","abstract":"

Redirects are not followed.

","parent_name":"RedirectConfiguration"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08RedirectC0V6follow3max11allowCyclesAGSi_SbtFZ":{"name":"follow(max:allowCycles:)","abstract":"

Redirects are followed with a specified limit.

","parent_name":"RedirectConfiguration"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV7connect3NIO10TimeAmountVSgvp":{"name":"connect","abstract":"

Specifies connect timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV4read3NIO10TimeAmountVSgvp":{"name":"read","abstract":"

Specifies read timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV7connect4readAG3NIO10TimeAmountVSg_AMtcfc":{"name":"init(connect:read:)","abstract":"

Create timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC06NIOSSL16TLSConfigurationVSgvp":{"name":"tlsConfiguration","abstract":"

TLS configuration, defaults to TLSConfiguration.forClient().

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08redirectC0AE08RedirectC0Vvp":{"name":"redirectConfiguration","abstract":"

Enables following 3xx redirects automatically, defaults to false.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7timeoutAE7TimeoutVvp":{"name":"timeout","abstract":"

Default client timeout, defaults to no timeouts.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5proxyAE5ProxyVSgvp":{"name":"proxy","abstract":"

Upstream proxy, defaults to no proxy.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV13decompressionAC13DecompressionOvp":{"name":"decompression","abstract":"

Enables automatic body decompression. Supported algorithms are gzip and deflate.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV24ignoreUncleanSSLShutdownSbvp":{"name":"ignoreUncleanSSLShutdown","abstract":"

Ignore TLS unclean shutdown error, defaults to false.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC008redirectC07timeout5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL16TLSConfigurationVSg_AE08RedirectC0VSgAE7TimeoutVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(tlsConfiguration:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/Timeout.html":{"name":"Timeout","abstract":"

Timeout configuration

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html":{"name":"RedirectConfiguration","abstract":"

Specifies redirect processing settings.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/Proxy.html":{"name":"Proxy","abstract":"

Proxy server configuration","parent_name":"Configuration"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C14eventLoopGroup3NIO05EventdE0_pvp":{"name":"eventLoopGroup","abstract":"

Undocumented

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C22eventLoopGroupProvider13configurationA2C05EventdeF0O_AC13ConfigurationVtcfc":{"name":"init(eventLoopGroupProvider:configuration:)","abstract":"

Create an HTTPClient with specified EventLoopGroup provider and configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C12syncShutdownyyKF":{"name":"syncShutdown()","abstract":"

Shuts down the client and EventLoopGroup if it was created by the client.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3get3url8deadline3NIO15EventLoopFutureCyAC8ResponseVGSS_AG11NIODeadlineVSgtF":{"name":"get(url:deadline:)","abstract":"

Execute GET request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C4post3url4body8deadline3NIO15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"post(url:body:deadline:)","abstract":"

Execute POST request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C5patch3url4body8deadline3NIO15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"patch(url:body:deadline:)","abstract":"

Execute PATCH request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3put3url4body8deadline3NIO15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"put(url:body:deadline:)","abstract":"

Execute PUT request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C6delete3url8deadline3NIO15EventLoopFutureCyAC8ResponseVGSS_AG11NIODeadlineVSgtF":{"name":"delete(url:deadline:)","abstract":"

Execute DELETE request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8deadline3NIO15EventLoopFutureCyAC8ResponseVGAC7RequestV_AG11NIODeadlineVSgtF":{"name":"execute(request:deadline:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request9eventLoop8deadline3NIO05EventF6FutureCyAC8ResponseVGAC7RequestV_AC0iF10PreferenceVAH11NIODeadlineVSgtF":{"name":"execute(request:eventLoop:deadline:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate8deadlineAC4TaskCy_8ResponseQzGAC7RequestV_x3NIO11NIODeadlineVSgtAA0bH8DelegateRzlF":{"name":"execute(request:delegate:deadline:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate9eventLoop8deadlineAC4TaskCy_8ResponseQzGAC7RequestV_xAC05EventG10PreferenceV3NIO11NIODeadlineVSgtAA0bJ8DelegateRzlF":{"name":"execute(request:delegate:eventLoop:deadline:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Configuration.html":{"name":"Configuration","abstract":"

HTTPClient configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient/EventLoopGroupProvider.html":{"name":"EventLoopGroupProvider","abstract":"

Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

","parent_name":"HTTPClient"},"Classes/HTTPClient/EventLoopPreference.html":{"name":"EventLoopPreference","abstract":"

Specifies how the library will treat event loop passed by the user.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Decompression.html":{"name":"Decompression","abstract":"

Specifies decompression settings.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Cookie.html":{"name":"Cookie","abstract":"

A representation of an HTTP cookie.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Response.html":{"name":"Response","abstract":"

Represent HTTP response.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Body.html":{"name":"Body","abstract":"

Represent request body.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Request.html":{"name":"Request","abstract":"

Represent HTTP request.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Authorization.html":{"name":"Authorization","abstract":"

HTTP authentication

","parent_name":"HTTPClient"},"Classes/HTTPClient/Task.html":{"name":"Task","abstract":"

Response execution context. Will be created by the library and could be used for obtaining","parent_name":"HTTPClient"},"Classes/HTTPClient.html":{"name":"HTTPClient","abstract":"

HTTPClient class provides API for request execution.

"},"Classes/ResponseAccumulator.html":{"name":"ResponseAccumulator","abstract":"

Undocumented

"},"Classes/HTTPClientCopyingDelegate.html":{"name":"HTTPClientCopyingDelegate","abstract":"

Undocumented

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"},"Structs.html":{"name":"Structures","abstract":"

The following structures are available globally.

"}} \ No newline at end of file diff --git a/docs/1.1.0/AsyncHTTPClient/undocumented.json b/docs/1.1.0/AsyncHTTPClient/undocumented.json new file mode 100644 index 000000000..996148270 --- /dev/null +++ b/docs/1.1.0/AsyncHTTPClient/undocumented.json @@ -0,0 +1,103 @@ +{ + "warnings": [ + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 49, + "symbol": "HTTPClient.eventLoopGroup", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 418, + "symbol": "HTTPClient.Configuration.init(tlsConfiguration:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 432, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 261, + "symbol": "HTTPClient.Authorization.basic(username:password:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 265, + "symbol": "HTTPClient.Authorization.basic(credentials:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 269, + "symbol": "HTTPClient.Authorization.bearer(tokens:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 273, + "symbol": "HTTPClient.Authorization.headerValue", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 284, + "symbol": "ResponseAccumulator", + "symbol_kind": "source.lang.swift.decl.class", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 285, + "symbol": "ResponseAccumulator.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 298, + "symbol": "ResponseAccumulator.init(request:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 367, + "symbol": "HTTPClientResponseDelegate.Response", + "symbol_kind": "source.lang.swift.decl.associatedtype", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 31, + "symbol": "HTTPClientCopyingDelegate", + "symbol_kind": "source.lang.swift.decl.class", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 32, + "symbol": "HTTPClientCopyingDelegate.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 36, + "symbol": "HTTPClientCopyingDelegate.init(chunkHandler:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + } + ], + "source_directory": "/code" +} \ No newline at end of file diff --git a/docs/1.2.3/AsyncHTTPClient/Classes.html b/docs/1.2.3/AsyncHTTPClient/Classes.html new file mode 100644 index 000000000..280f48d51 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Classes.html @@ -0,0 +1,318 @@ + + + + Classes Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Classes

+

The following classes are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + FileDownloadDelegate + +
    +
    +
    +
    +
    +
    +

    Handles a streaming download to a given file path, allowing headers and progress to be reported.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class FileDownloadDelegate : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + HTTPClient + +
    +
    +
    +
    +
    +
    +

    HTTPClient class provides API for request execution.

    + +

    Example:

    +
        let client = HTTPClient(eventLoopGroupProvider: .createNew)
    +    client.get(url: "https://swift.org/", deadline: .now() + .seconds(1)).whenComplete { result in
    +        switch result {
    +        case .failure(let error):
    +            // process error
    +        case .success(let response):
    +            if let response.status == .ok {
    +                // handle response
    +            } else {
    +                // handle remote error
    +            }
    +        }
    +    }
    +
    + +

    It is important to close the client instance, for example in a defer statement, after use to cleanly shutdown the underlying NIO EventLoopGroup:

    +
        try client.syncShutdown()
    +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class HTTPClient
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + ResponseAccumulator + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class ResponseAccumulator : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Undocumented

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class HTTPClientCopyingDelegate : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.2.3/AsyncHTTPClient/Classes/FileDownloadDelegate.html b/docs/1.2.3/AsyncHTTPClient/Classes/FileDownloadDelegate.html new file mode 100644 index 000000000..da4e8001f --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Classes/FileDownloadDelegate.html @@ -0,0 +1,461 @@ + + + + FileDownloadDelegate Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

FileDownloadDelegate

+
+
+ +
public final class FileDownloadDelegate : HTTPClientResponseDelegate
+ +
+
+

Handles a streaming download to a given file path, allowing headers and progress to be reported.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + Progress + +
    +
    +
    +
    +
    +
    +

    The response type for this delegate: the total count of bytes as reported by the response +“Content-Length” header (if available) and the count of bytes downloaded.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Progress
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public typealias Response = Progress
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Initializes a new file download delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(
    +    path: String,
    +    pool: NIOThreadPool = NIOThreadPool(numberOfThreads: 1),
    +    reportHead: ((HTTPResponseHead) -> Void)? = nil,
    +    reportProgress: ((Progress) -> Void)? = nil
    +) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + path + + +
    +

    Path to a file you’d like to write the download to.

    +
    +
    + + pool + + +
    +

    A thread pool to use for asynchronous file I/O.

    +
    +
    + + reportHead + + +
    +

    A closure called when the response head is available.

    +
    +
    + + reportProgress + + +
    +

    A closure called when a body chunk has been downloaded, with +the total byte count and download byte count passed to it as arguments. The callbacks +will be invoked in the same threading context that the delegate itself is invoked, +as controlled by EventLoopPreference.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveHead(
    +    task: HTTPClient.Task<Response>,
    +    _ head: HTTPResponseHead
    +) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveBodyPart(
    +    task: HTTPClient.Task<Response>,
    +    _ buffer: ByteBuffer
    +) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveError(task: HTTPClient.Task<Progress>, _ error: Error)
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didFinishRequest(task: HTTPClient.Task<Response>) throws -> Response
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.2.3/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html b/docs/1.2.3/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html new file mode 100644 index 000000000..e5e625b80 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html @@ -0,0 +1,245 @@ + + + + Progress Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Progress

+
+
+ +
public struct Progress
+ +
+
+

The response type for this delegate: the total count of bytes as reported by the response +“Content-Length” header (if available) and the count of bytes downloaded.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + totalBytes + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var totalBytes: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + receivedBytes + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var receivedBytes: Int
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient.html b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient.html new file mode 100644 index 000000000..5390e6ef5 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient.html @@ -0,0 +1,2438 @@ + + + + HTTPClient Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClient

+
+
+ +
public class HTTPClient
+ +
+
+

HTTPClient class provides API for request execution.

+ +

Example:

+
    let client = HTTPClient(eventLoopGroupProvider: .createNew)
+    client.get(url: "https://swift.org/", deadline: .now() + .seconds(1)).whenComplete { result in
+        switch result {
+        case .failure(let error):
+            // process error
+        case .success(let response):
+            if let response.status == .ok {
+                // handle response
+            } else {
+                // handle remote error
+            }
+        }
+    }
+
+ +

It is important to close the client instance, for example in a defer statement, after use to cleanly shutdown the underlying NIO EventLoopGroup:

+
    try client.syncShutdown()
+
+ + +
+
+ +
+
+
+
    +
  • +
    + + + + eventLoopGroup + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let eventLoopGroup: EventLoopGroup
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTPClient with specified EventLoopGroup provider and configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public convenience init(eventLoopGroupProvider: EventLoopGroupProvider,
    +                        configuration: Configuration = Configuration())
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + eventLoopGroupProvider + + +
    +

    Specify how EventLoopGroup will be created.

    +
    +
    + + configuration + + +
    +

    Client configuration.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTPClient with specified EventLoopGroup provider and configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public required init(eventLoopGroupProvider: EventLoopGroupProvider,
    +                     configuration: Configuration = Configuration(),
    +                     backgroundActivityLogger: Logger)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + eventLoopGroupProvider + + +
    +

    Specify how EventLoopGroup will be created.

    +
    +
    + + configuration + + +
    +

    Client configuration.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + syncShutdown() + +
    +
    +
    +
    +
    +
    +

    Shuts down the client and EventLoopGroup if it was created by the client.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func syncShutdown() throws
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + shutdown(queue:_:) + +
    +
    +
    +
    +
    +
    +

    Shuts down the client and event loop gracefully. This function is clearly an outlier in that it uses a completion +callback instead of an EventLoopFuture. The reason for that is that NIO’s EventLoopFutures will call back on an event loop. +The virtue of this function is to shut the event loop down. To work around that we call back on a DispatchQueue +instead.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func shutdown(queue: DispatchQueue = .global(), _ callback: @escaping (Error?) -> Void)
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + get(url:deadline:) + +
    +
    +
    +
    +
    +
    +

    Execute GET request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func get(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute GET request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func get(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute POST request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute POST request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PATCH request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PATCH request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PUT request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PUT request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + delete(url:deadline:) + +
    +
    +
    +
    +
    +
    +

    Execute DELETE request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func delete(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    The time when the request must have been completed by.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute DELETE request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func delete(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    The time when the request must have been completed by.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + url + + +
    +

    Request url.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP+UNIX request to a unix domain socket path, using the specified URL as the request to send to the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, socketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + urlPath + + +
    +

    The URL path and query that will be sent to the server.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTPS+UNIX request to a unix domain socket path over TLS, using the specified URL as the request to send to the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, secureSocketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + secureSocketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + urlPath + + +
    +

    The URL path and query that will be sent to the server.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, eventLoop: EventLoopPreference, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request,
    +                    eventLoop eventLoopPreference: EventLoopPreference,
    +                    deadline: NIODeadline? = nil,
    +                    logger: Logger?) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          deadline: NIODeadline? = nil) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          deadline: NIODeadline? = nil,
    +                                                          logger: Logger) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          eventLoop eventLoopPreference: EventLoopPreference,
    +                                                          deadline: NIODeadline? = nil) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          eventLoop eventLoopPreference: EventLoopPreference,
    +                                                          deadline: NIODeadline? = nil,
    +                                                          logger originalLogger: Logger?) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + Configuration + +
    +
    +
    +
    +
    +
    +

    HTTPClient configuration.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Configuration
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum EventLoopGroupProvider
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + EventLoopPreference + +
    +
    +
    +
    +
    +
    +

    Specifies how the library will treat event loop passed by the user.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct EventLoopPreference
    +
    extension HTTPClient.EventLoopPreference: CustomStringConvertible
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Decompression + +
    +
    +
    +
    +
    +
    +

    Specifies decompression settings.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum Decompression
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Cookie + +
    +
    +
    +
    +
    +
    +

    A representation of an HTTP cookie.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Cookie
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Represent HTTP response.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Response
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Body + +
    +
    +
    +
    +
    +
    +

    Represent request body.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Body
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Request + +
    +
    +
    +
    +
    +
    +

    Represent HTTP request.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Request
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Authorization + +
    +
    +
    +
    +
    +
    +

    HTTP authentication

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Authorization
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Task + +
    +
    +
    +
    +
    +
    +

    Response execution context. Will be created by the library and could be used for obtaining +EventLoopFuture<Response> of the execution or cancellation of the execution.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class Task<Response>
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Authorization.html b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Authorization.html new file mode 100644 index 000000000..5fc78f2cd --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Authorization.html @@ -0,0 +1,304 @@ + + + + Authorization Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Authorization

+
+
+ +
public struct Authorization
+ +
+
+

HTTP authentication

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + +
+ diff --git a/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Body.html b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Body.html new file mode 100644 index 000000000..d9347b655 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Body.html @@ -0,0 +1,485 @@ + + + + Body Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Body

+
+
+ +
public struct Body
+ +
+
+

Represent request body.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + StreamWriter + +
    +
    +
    +
    +
    +
    +

    Chunk provider.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct StreamWriter
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + length + +
    +
    +
    +
    +
    +
    +

    Body size. Request validation will be failed with HTTPClientErrors.contentLengthMissing if nil, +unless Trasfer-Encoding: chunked header is set.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var length: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + stream + +
    +
    +
    +
    +
    +
    +

    Body chunk provider.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var stream: (StreamWriter) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + byteBuffer(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using ByteBuffer.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func byteBuffer(_ buffer: ByteBuffer) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + buffer + + +
    +

    Body ByteBuffer representation.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + stream(length:_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using StreamWriter.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func stream(length: Int? = nil, _ stream: @escaping (StreamWriter) -> EventLoopFuture<Void>) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + length + + +
    +

    Body size. Request validation will be failed with HTTPClientErrors.contentLengthMissing if nil, + unless Transfer-Encoding: chunked header is set.

    +
    +
    + + stream + + +
    +

    Body chunk provider.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + data(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using Data.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func data(_ data: Data) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    Body Data representation.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + string(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using String.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func string(_ string: String) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + string + + +
    +

    Body String representation.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html new file mode 100644 index 000000000..328db8656 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html @@ -0,0 +1,282 @@ + + + + StreamWriter Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

StreamWriter

+
+
+ +
public struct StreamWriter
+ +
+
+

Chunk provider.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + init(closure:) + +
    +
    +
    +
    +
    +
    +

    Create new StreamWriter

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(closure: @escaping (IOData) -> EventLoopFuture<Void>)
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + closure + + +
    +

    function that will be called to write actual bytes to the channel.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + write(_:) + +
    +
    +
    +
    +
    +
    +

    Write data to server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func write(_ data: IOData) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    IOData to write.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Configuration.html b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Configuration.html new file mode 100644 index 000000000..ad43d98c5 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Configuration.html @@ -0,0 +1,718 @@ + + + + Configuration Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Configuration

+
+
+ +
public struct Configuration
+ +
+
+

HTTPClient configuration.

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html new file mode 100644 index 000000000..4a2a1701c --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html @@ -0,0 +1,244 @@ + + + + ConnectionPool Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

ConnectionPool

+
+
+ +
public struct ConnectionPool : Hashable
+ +
+
+

Connection pool configuration.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + idleTimeout + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var idleTimeout: TimeAmount
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + init(idleTimeout:) + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(idleTimeout: TimeAmount = .seconds(60))
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html new file mode 100644 index 000000000..9c8ea9ae8 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html @@ -0,0 +1,417 @@ + + + + Proxy Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Proxy

+
+
+ +
struct Proxy
+ +
+
+

Proxy server configuration +Specifies the remote address of an HTTP proxy.

+ +

Adding an Proxy to your client’s HTTPClient.Configuration +will cause requests to be passed through the specified proxy using the +HTTP CONNECT method.

+ +

If a TLSConfiguration is used in conjunction with HTTPClient.Configuration.Proxy, +TLS will be established after successful proxy, between your client +and the destination server.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server host.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + port + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server port.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var port: Int
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + authorization + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server authorization.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var authorization: HTTPClient.Authorization?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + server(host:port:) + +
    +
    +
    +
    +
    +
    +

    Create proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func server(host: String, port: Int) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + host + + +
    +

    proxy server host.

    +
    +
    + + port + + +
    +

    proxy server port.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func server(host: String, port: Int, authorization: HTTPClient.Authorization? = nil) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + host + + +
    +

    proxy server host.

    +
    +
    + + port + + +
    +

    proxy server port.

    +
    +
    + + authorization + + +
    +

    proxy server authorization.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html new file mode 100644 index 000000000..a910c4c00 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html @@ -0,0 +1,280 @@ + + + + RedirectConfiguration Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

RedirectConfiguration

+
+
+ +
public struct RedirectConfiguration
+ +
+
+

Specifies redirect processing settings.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + disallow + +
    +
    +
    +
    +
    +
    +

    Redirects are not followed.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let disallow: HTTPClient.Configuration.RedirectConfiguration
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Redirects are followed with a specified limit.

    +
    +

    Warning

    +

    Cycle detection will keep all visited URLs in memory which means a malicious server could use this as a denial-of-service vector.

    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func follow(max: Int, allowCycles: Bool) -> RedirectConfiguration
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + max + + +
    +

    The maximum number of allowed redirects.

    +
    +
    + + allowCycles + + +
    +

    Whether cycles are allowed.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html new file mode 100644 index 000000000..7989ad678 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html @@ -0,0 +1,305 @@ + + + + Timeout Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Timeout

+
+
+ +
public struct Timeout
+ +
+
+

Timeout configuration.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + connect + +
    +
    +
    +
    +
    +
    +

    Specifies connect timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var connect: TimeAmount?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + read + +
    +
    +
    +
    +
    +
    +

    Specifies read timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var read: TimeAmount?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + init(connect:read:) + +
    +
    +
    +
    +
    +
    +

    Create timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(connect: TimeAmount? = nil, read: TimeAmount? = nil)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + connect + + +
    +

    connect timeout.

    +
    +
    + + read + + +
    +

    read timeout.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Cookie.html b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Cookie.html new file mode 100644 index 000000000..4c56d8ff4 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Cookie.html @@ -0,0 +1,622 @@ + + + + Cookie Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Cookie

+
+
+ +
public struct Cookie
+ +
+
+

A representation of an HTTP cookie.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + name + +
    +
    +
    +
    +
    +
    +

    The name of the cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var name: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + value + +
    +
    +
    +
    +
    +
    +

    The cookie’s string value.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var value: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + path + +
    +
    +
    +
    +
    +
    +

    The cookie’s path.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var path: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + domain + +
    +
    +
    +
    +
    +
    +

    The domain of the cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var domain: String?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + expires + +
    +
    +
    +
    +
    +
    +

    The cookie’s expiration date.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var expires: Date?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + maxAge + +
    +
    +
    +
    +
    +
    +

    The cookie’s age in seconds.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var maxAge: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + httpOnly + +
    +
    +
    +
    +
    +
    +

    Whether the cookie should only be sent to HTTP servers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var httpOnly: Bool
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + secure + +
    +
    +
    +
    +
    +
    +

    Whether the cookie should only be sent over secure channels.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var secure: Bool
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a Cookie by parsing a Set-Cookie header.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(header: String, defaultDomain: String)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + header + + +
    +

    String representation of the Set-Cookie response header.

    +
    +
    + + defaultDomain + + +
    +

    Default domain to use if cookie was sent without one.

    +
    +
    +
    +
    +

    Return Value

    +

    nil if the header is invalid.

    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(name: String, value: String, path: String = "/", domain: String? = nil, expires: Date? = nil, maxAge: Int? = nil, httpOnly: Bool = false, secure: Bool = false)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + name + + +
    +

    The name of the cookie.

    +
    +
    + + value + + +
    +

    The cookie’s string value.

    +
    +
    + + path + + +
    +

    The cookie’s path.

    +
    +
    + + domain + + +
    +

    The domain of the cookie, defaults to nil.

    +
    +
    + + expires + + +
    +

    The cookie’s expiration date, defaults to nil.

    +
    +
    + + maxAge + + +
    +

    The cookie’s age in seconds, defaults to nil.

    +
    +
    + + httpOnly + + +
    +

    Whether this cookie should be used by HTTP servers only, defaults to false.

    +
    +
    + + secure + + +
    +

    Whether this cookie should only be sent using secure channels, defaults to false.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Decompression.html b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Decompression.html new file mode 100644 index 000000000..decdd55f2 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Decompression.html @@ -0,0 +1,244 @@ + + + + Decompression Enumeration Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Decompression

+
+
+ +
public enum Decompression
+ +
+
+

Specifies decompression settings.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + disabled + +
    +
    +
    +
    +
    +
    +

    Decompression is disabled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case disabled
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + enabled(limit:) + +
    +
    +
    +
    +
    +
    +

    Decompression is enabled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case enabled(limit: NIOHTTPDecompression.DecompressionLimit)
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html new file mode 100644 index 000000000..9ee10ab21 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html @@ -0,0 +1,244 @@ + + + + EventLoopGroupProvider Enumeration Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

EventLoopGroupProvider

+
+
+ +
public enum EventLoopGroupProvider
+ +
+
+

Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + shared(_:) + +
    +
    +
    +
    +
    +
    +

    EventLoopGroup will be provided by the user. Owner of this group is responsible for its lifecycle.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case shared(EventLoopGroup)
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + createNew + +
    +
    +
    +
    +
    +
    +

    EventLoopGroup will be created by the client. When syncShutdown is called, created EventLoopGroup will be shut down as well.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case createNew
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html new file mode 100644 index 000000000..20238b8fd --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html @@ -0,0 +1,311 @@ + + + + EventLoopPreference Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

EventLoopPreference

+
+
+ +
public struct EventLoopPreference
+
extension HTTPClient.EventLoopPreference: CustomStringConvertible
+ +
+
+

Specifies how the library will treat event loop passed by the user.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + indifferent + +
    +
    +
    +
    +
    +
    +

    Event Loop will be selected by the library.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let indifferent: HTTPClient.EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + delegate(on:) + +
    +
    +
    +
    +
    +
    +

    The delegate will be run on the specified EventLoop (and the Channel if possible).

    + +

    This will call the configured delegate on eventLoop and will try to use a Channel on the same +EventLoop but will not establish a new network connection just to satisfy the EventLoop preference if +another existing connection on a different EventLoop is readily available from a connection pool.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func delegate(on eventLoop: EventLoop) -> EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The delegate and the Channel will be run on the specified EventLoop.

    + +

    Use this for use-cases where you prefer a new connection to be established over re-using an existing +connection that might be on a different EventLoop.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func delegateAndChannel(on eventLoop: EventLoop) -> EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var description: String { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Request.html b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Request.html new file mode 100644 index 000000000..01d49e211 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Request.html @@ -0,0 +1,626 @@ + + + + Request Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Request

+
+
+ +
public struct Request
+ +
+
+

Represent HTTP request.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + method + +
    +
    +
    +
    +
    +
    +

    Request HTTP method, defaults to GET.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let method: HTTPMethod
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + url + +
    +
    +
    +
    +
    +
    +

    Remote URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let url: URL
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + scheme + +
    +
    +
    +
    +
    +
    +

    Remote HTTP scheme, resolved from URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let scheme: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Remote host, resolved from URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + headers + +
    +
    +
    +
    +
    +
    +

    Request custom HTTP Headers, defaults to no headers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var headers: HTTPHeaders
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + body + +
    +
    +
    +
    +
    +
    +

    Request body, defaults to no body.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var body: Body?
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP request.

    +
    +

    Throws

    +
      +
    • invalidURL if URL cannot be parsed.
    • +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: String, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + version + + +
    +

    HTTP version.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTP Request.

    +
    +

    Throws

    +
      +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    • missingSocketPath if URL does not contains a socketPath as an encoded host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + useTLS + +
    +
    +
    +
    +
    +
    +

    Whether request will be executed using secure socket.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var useTLS: Bool { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + port + +
    +
    +
    +
    +
    +
    +

    Resolved port.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var port: Int { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Response.html b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Response.html new file mode 100644 index 000000000..c7e41971b --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Response.html @@ -0,0 +1,547 @@ + + + + Response Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Response

+
+
+ +
public struct Response
+ +
+
+

Represent HTTP response.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Remote host of the request.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + status + +
    +
    +
    +
    +
    +
    +

    Response HTTP status.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var status: HTTPResponseStatus
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + version + +
    +
    +
    +
    +
    +
    +

    Response HTTP version.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var version: HTTPVersion
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + headers + +
    +
    +
    +
    +
    +
    +

    Reponse HTTP headers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var headers: HTTPHeaders
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + body + +
    +
    +
    +
    +
    +
    +

    Response body.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var body: ByteBuffer?
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP Response.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    @available(*, deprecated, renamed: "init(host:status:version:headers:body:﹚")
    +public init(host: String, status: HTTPResponseStatus, headers: HTTPHeaders, body: ByteBuffer?)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + host + + +
    +

    Remote host of the request.

    +
    +
    + + status + + +
    +

    Response HTTP status.

    +
    +
    + + headers + + +
    +

    Reponse HTTP headers.

    +
    +
    + + body + + +
    +

    Response body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP Response.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(host: String, status: HTTPResponseStatus, version: HTTPVersion, headers: HTTPHeaders, body: ByteBuffer?)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + host + + +
    +

    Remote host of the request.

    +
    +
    + + status + + +
    +

    Response HTTP status.

    +
    +
    + + version + + +
    +

    Response HTTP version.

    +
    +
    + + headers + + +
    +

    Reponse HTTP headers.

    +
    +
    + + body + + +
    +

    Response body.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + cookies + +
    +
    +
    +
    +
    +
    +

    List of HTTP cookies returned by the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var cookies: [HTTPClient.Cookie] { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Task.html b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Task.html new file mode 100644 index 000000000..a8bf5e74b --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClient/Task.html @@ -0,0 +1,314 @@ + + + + Task Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Task

+
+
+ +
public final class Task<Response>
+ +
+
+

Response execution context. Will be created by the library and could be used for obtaining +EventLoopFuture<Response> of the execution or cancellation of the execution.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + eventLoop + +
    +
    +
    +
    +
    +
    +

    The EventLoop the delegate will be executed on.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let eventLoop: EventLoop
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + futureResult + +
    +
    +
    +
    +
    +
    +

    EventLoopFuture for the response returned by this request.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var futureResult: EventLoopFuture<Response> { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + wait() + +
    +
    +
    +
    +
    +
    +

    Waits for execution of this request to complete.

    +
    +

    Throws

    + The error value of the EventLoopFuture if it errors. + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func wait() throws -> Response
    + +
    +
    +
    +

    Return Value

    +

    The value of the EventLoopFuture when it completes.

    +
    + +
    +
    +
  • +
  • +
    + + + + cancel() + +
    +
    +
    +
    +
    +
    +

    Cancels the request execution.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func cancel()
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html new file mode 100644 index 000000000..84e71101c --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html @@ -0,0 +1,302 @@ + + + + HTTPClientCopyingDelegate Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientCopyingDelegate

+
+
+ +
public final class HTTPClientCopyingDelegate : HTTPClientResponseDelegate
+ +
+
+

Undocumented

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/Classes/ResponseAccumulator.html b/docs/1.2.3/AsyncHTTPClient/Classes/ResponseAccumulator.html new file mode 100644 index 000000000..53cbfe703 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Classes/ResponseAccumulator.html @@ -0,0 +1,360 @@ + + + + ResponseAccumulator Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

ResponseAccumulator

+
+
+ +
public class ResponseAccumulator : HTTPClientResponseDelegate
+ +
+
+

Undocumented

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/Extensions.html b/docs/1.2.3/AsyncHTTPClient/Extensions.html new file mode 100644 index 000000000..b2197b6a5 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Extensions.html @@ -0,0 +1,238 @@ + + + + Extensions Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Extensions

+

The following extensions are available globally.

+ +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/Extensions/HTTPClient.html b/docs/1.2.3/AsyncHTTPClient/Extensions/HTTPClient.html new file mode 100644 index 000000000..8d7e62253 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Extensions/HTTPClient.html @@ -0,0 +1,211 @@ + + + + HTTPClient Extension Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ + +
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/Extensions/HTTPClient/NWPOSIXError.html b/docs/1.2.3/AsyncHTTPClient/Extensions/HTTPClient/NWPOSIXError.html new file mode 100644 index 000000000..ba20407b0 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Extensions/HTTPClient/NWPOSIXError.html @@ -0,0 +1,229 @@ + + + + NWPOSIXError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

NWPOSIXError

+ +
+
+ +
+
+
+
    +
  • +
    + + + + errorCode + +
    +
    +
    +
    +
    +
    +

    POSIX error code (enum)

    + +
    +
    +
    +
  • +
  • +
    + + + + init(_:reason:) + +
    +
    +
    +
    +
    +
    +

    Initialise a NWPOSIXError

    + +
    +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/Extensions/HTTPClient/NWTLSError.html b/docs/1.2.3/AsyncHTTPClient/Extensions/HTTPClient/NWTLSError.html new file mode 100644 index 000000000..13d34599d --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Extensions/HTTPClient/NWTLSError.html @@ -0,0 +1,229 @@ + + + + NWTLSError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

NWTLSError

+ +
+
+ +
+
+
+
    +
  • +
    + + + + status + +
    +
    +
    +
    +
    +
    +

    TLS error status. List of TLS errors can be found in

    + +
    +
    +
    +
  • +
  • +
    + + + + init(_:reason:) + +
    +
    +
    +
    +
    +
    +

    initialise a NWTLSError

    + +
    +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/Extensions/URL.html b/docs/1.2.3/AsyncHTTPClient/Extensions/URL.html new file mode 100644 index 000000000..15cf305df --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Extensions/URL.html @@ -0,0 +1,302 @@ + + + + URL Extension Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

URL

+
+
+ +
extension URL
+ +
+
+ +
+
+ +
+
+
+
    +
  • + +
    +
    +
    +
    +
    +

    Initializes a newly created HTTP URL connecting to a unix domain socket path. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “http+unix” scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(httpURLWithSocketPath socketPath: String, uri: String = "/")
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + uri + + +
    +

    The URI path and query that will be sent to the server.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Initializes a newly created HTTPS URL connecting to a unix domain socket path over TLS. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “https+unix” scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(httpsURLWithSocketPath socketPath: String, uri: String = "/")
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + uri + + +
    +

    The URI path and query that will be sent to the server.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/Protocols.html b/docs/1.2.3/AsyncHTTPClient/Protocols.html new file mode 100644 index 000000000..43a810dd6 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Protocols.html @@ -0,0 +1,217 @@ + + + + Protocols Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Protocols

+

The following protocols are available globally.

+ +
+
+ +
+
+
+
    +
  • + +
    +
    +
    +
    +
    +

    HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed. +You can implement this protocol if you need fine-grained control over an HTTP request/response, for example, if you want to inspect the response +headers before deciding whether to accept a response body, or if you want to stream your request body. Pass an instance of your conforming +class to the HTTPClient.execute() method and this package will call each delegate method appropriately as the request takes place.

    +
    +

    Note

    + This delegate is strongly held by the HTTPTaskHandler + for the duration of the Request processing and will be + released together with the HTTPTaskHandler when channel is closed. + Users of the library are not required to keep a reference to the + object that implements this protocol, but may do so if needed. + +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public protocol HTTPClientResponseDelegate : AnyObject
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html b/docs/1.2.3/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html new file mode 100644 index 000000000..79e716e83 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html @@ -0,0 +1,687 @@ + + + + HTTPClientResponseDelegate Protocol Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientResponseDelegate

+
+
+ +
public protocol HTTPClientResponseDelegate : AnyObject
+ +
+
+

HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed. +You can implement this protocol if you need fine-grained control over an HTTP request/response, for example, if you want to inspect the response +headers before deciding whether to accept a response body, or if you want to stream your request body. Pass an instance of your conforming +class to the HTTPClient.execute() method and this package will call each delegate method appropriately as the request takes place.

+
+

Note

+ This delegate is strongly held by the HTTPTaskHandler + for the duration of the Request processing and will be + released together with the HTTPTaskHandler when channel is closed. + Users of the library are not required to keep a reference to the + object that implements this protocol, but may do so if needed. + +
+ + +
+
+ +
+
+
+
    +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    associatedtype Response
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequestHead(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when the request head is sent. Will be called once.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequestHead(task: HTTPClient.Task<Response>, _ head: HTTPRequestHead)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + head + + +
    +

    Request head.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequestPart(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when a part of the request body is sent. Could be called zero or more times.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequestPart(task: HTTPClient.Task<Response>, _ part: IOData)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + part + + +
    +

    Request body Part.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequest(task:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when the request is fully sent. Will be called once.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequest(task: HTTPClient.Task<Response>)
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveHead(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when response head is received. Will be called once. +You must return an EventLoopFuture<Void> that you complete when you have finished processing the body part. +You can create an already succeeded future by calling task.eventLoop.makeSucceededFuture(()).

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveHead(task: HTTPClient.Task<Response>, _ head: HTTPResponseHead) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + head + + +
    +

    Received reposonse head.

    +
    +
    +
    +
    +

    Return Value

    +

    EventLoopFuture that will be used for backpressure.

    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveBodyPart(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when part of a response body is received. Could be called zero or more times. +You must return an EventLoopFuture<Void> that you complete when you have finished processing the body part. +You can create an already succeeded future by calling task.eventLoop.makeSucceededFuture(()).

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveBodyPart(task: HTTPClient.Task<Response>, _ buffer: ByteBuffer) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + buffer + + +
    +

    Received body Part.

    +
    +
    +
    +
    +

    Return Value

    +

    EventLoopFuture that will be used for backpressure.

    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveError(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when error was thrown during request execution. Will be called zero or one time only. Request processing will be stopped after that.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveError(task: HTTPClient.Task<Response>, _ error: Error)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + error + + +
    +

    Error that occured during response processing.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Called when the complete HTTP request is finished. You must return an instance of your Response associated type. Will be called once, except if an error occurred.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didFinishRequest(task: HTTPClient.Task<Response>) throws -> Response
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    +
    +
    +

    Return Value

    +

    Result of processing.

    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/Structs.html b/docs/1.2.3/AsyncHTTPClient/Structs.html new file mode 100644 index 000000000..88d7b00f6 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Structs.html @@ -0,0 +1,205 @@ + + + + Structures Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Structures

+

The following structures are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + HTTPClientError + +
    +
    +
    +
    +
    +
    +

    Possible client errors.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct HTTPClientError : Error, Equatable, CustomStringConvertible
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/Structs/HTTPClientError.html b/docs/1.2.3/AsyncHTTPClient/Structs/HTTPClientError.html new file mode 100644 index 000000000..eadf9aa44 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/Structs/HTTPClientError.html @@ -0,0 +1,873 @@ + + + + HTTPClientError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientError

+
+
+ +
public struct HTTPClientError : Error, Equatable, CustomStringConvertible
+ +
+
+

Possible client errors.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var description: String { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + invalidURL + +
    +
    +
    +
    +
    +
    +

    URL provided is invalid.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let invalidURL: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + emptyHost + +
    +
    +
    +
    +
    +
    +

    URL does not contain host.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let emptyHost: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + missingSocketPath + +
    +
    +
    +
    +
    +
    +

    URL does not contain a socketPath as a host for http(s)+unix shemes.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let missingSocketPath: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + alreadyShutdown + +
    +
    +
    +
    +
    +
    +

    Client is shutdown and cannot be used for new requests.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let alreadyShutdown: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + emptyScheme + +
    +
    +
    +
    +
    +
    +

    URL does not contain scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let emptyScheme: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + unsupportedScheme(_:) + +
    +
    +
    +
    +
    +
    +

    Provided URL scheme is not supported, supported schemes are: http and https

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func unsupportedScheme(_ scheme: String) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + readTimeout + +
    +
    +
    +
    +
    +
    +

    Request timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let readTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Remote connection was closed unexpectedly.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let remoteConnectionClosed: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + cancelled + +
    +
    +
    +
    +
    +
    +

    Request was cancelled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let cancelled: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Request contains invalid identity encoding.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let identityCodingIncorrectlyPresent: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Request contains multiple chunks definitions.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let chunkedSpecifiedMultipleTimes: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + invalidProxyResponse + +
    +
    +
    +
    +
    +
    +

    Proxy response was invalid.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let invalidProxyResponse: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + contentLengthMissing + +
    +
    +
    +
    +
    +
    +

    Request does not contain Content-Length header.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let contentLengthMissing: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Proxy Authentication Required.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let proxyAuthenticationRequired: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + redirectLimitReached + +
    +
    +
    +
    +
    +
    +

    Redirect Limit reached.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let redirectLimitReached: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + redirectCycleDetected + +
    +
    +
    +
    +
    +
    +

    Redirect Cycle detected.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let redirectCycleDetected: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + uncleanShutdown + +
    +
    +
    +
    +
    +
    +

    Unclean shutdown.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let uncleanShutdown: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + traceRequestWithBody + +
    +
    +
    +
    +
    +
    +

    A body was sent in a request with method TRACE.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let traceRequestWithBody: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Header field names contain invalid characters.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func invalidHeaderFieldNames(_ names: [String]) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + bodyLengthMismatch + +
    +
    +
    +
    +
    +
    +

    Body length is not equal to Content-Length.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let bodyLengthMismatch: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + writeAfterRequestSent + +
    +
    +
    +
    +
    +
    +

    Body part was written after request was fully sent.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let writeAfterRequestSent: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + incompatibleHeaders + +
    +
    +
    +
    +
    +
    +

    Incompatible headers specified, for example Transfer-Encoding and Content-Length.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let incompatibleHeaders: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/badge.svg b/docs/1.2.3/AsyncHTTPClient/badge.svg new file mode 100644 index 000000000..da0aa9790 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/badge.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + documentation + + + documentation + + + 88% + + + 88% + + + diff --git a/docs/1.2.3/AsyncHTTPClient/css/highlight.css b/docs/1.2.3/AsyncHTTPClient/css/highlight.css new file mode 100644 index 000000000..d0db0e13b --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/css/highlight.css @@ -0,0 +1,200 @@ +/* Credit to https://gist.github.com/wataru420/2048287 */ +.highlight { + /* Comment */ + /* Error */ + /* Keyword */ + /* Operator */ + /* Comment.Multiline */ + /* Comment.Preproc */ + /* Comment.Single */ + /* Comment.Special */ + /* Generic.Deleted */ + /* Generic.Deleted.Specific */ + /* Generic.Emph */ + /* Generic.Error */ + /* Generic.Heading */ + /* Generic.Inserted */ + /* Generic.Inserted.Specific */ + /* Generic.Output */ + /* Generic.Prompt */ + /* Generic.Strong */ + /* Generic.Subheading */ + /* Generic.Traceback */ + /* Keyword.Constant */ + /* Keyword.Declaration */ + /* Keyword.Pseudo */ + /* Keyword.Reserved */ + /* Keyword.Type */ + /* Literal.Number */ + /* Literal.String */ + /* Name.Attribute */ + /* Name.Builtin */ + /* Name.Class */ + /* Name.Constant */ + /* Name.Entity */ + /* Name.Exception */ + /* Name.Function */ + /* Name.Namespace */ + /* Name.Tag */ + /* Name.Variable */ + /* Operator.Word */ + /* Text.Whitespace */ + /* Literal.Number.Float */ + /* Literal.Number.Hex */ + /* Literal.Number.Integer */ + /* Literal.Number.Oct */ + /* Literal.String.Backtick */ + /* Literal.String.Char */ + /* Literal.String.Doc */ + /* Literal.String.Double */ + /* Literal.String.Escape */ + /* Literal.String.Heredoc */ + /* Literal.String.Interpol */ + /* Literal.String.Other */ + /* Literal.String.Regex */ + /* Literal.String.Single */ + /* Literal.String.Symbol */ + /* Name.Builtin.Pseudo */ + /* Name.Variable.Class */ + /* Name.Variable.Global */ + /* Name.Variable.Instance */ + /* Literal.Number.Integer.Long */ } + .highlight .c { + color: #999988; + font-style: italic; } + .highlight .err { + color: #a61717; + background-color: #e3d2d2; } + .highlight .k { + color: #000000; + font-weight: bold; } + .highlight .o { + color: #000000; + font-weight: bold; } + .highlight .cm { + color: #999988; + font-style: italic; } + .highlight .cp { + color: #999999; + font-weight: bold; } + .highlight .c1 { + color: #999988; + font-style: italic; } + .highlight .cs { + color: #999999; + font-weight: bold; + font-style: italic; } + .highlight .gd { + color: #000000; + background-color: #ffdddd; } + .highlight .gd .x { + color: #000000; + background-color: #ffaaaa; } + .highlight .ge { + color: #000000; + font-style: italic; } + .highlight .gr { + color: #aa0000; } + .highlight .gh { + color: #999999; } + .highlight .gi { + color: #000000; + background-color: #ddffdd; } + .highlight .gi .x { + color: #000000; + background-color: #aaffaa; } + .highlight .go { + color: #888888; } + .highlight .gp { + color: #555555; } + .highlight .gs { + font-weight: bold; } + .highlight .gu { + color: #aaaaaa; } + .highlight .gt { + color: #aa0000; } + .highlight .kc { + color: #000000; + font-weight: bold; } + .highlight .kd { + color: #000000; + font-weight: bold; } + .highlight .kp { + color: #000000; + font-weight: bold; } + .highlight .kr { + color: #000000; + font-weight: bold; } + .highlight .kt { + color: #445588; } + .highlight .m { + color: #009999; } + .highlight .s { + color: #d14; } + .highlight .na { + color: #008080; } + .highlight .nb { + color: #0086B3; } + .highlight .nc { + color: #445588; + font-weight: bold; } + .highlight .no { + color: #008080; } + .highlight .ni { + color: #800080; } + .highlight .ne { + color: #990000; + font-weight: bold; } + .highlight .nf { + color: #990000; } + .highlight .nn { + color: #555555; } + .highlight .nt { + color: #000080; } + .highlight .nv { + color: #008080; } + .highlight .ow { + color: #000000; + font-weight: bold; } + .highlight .w { + color: #bbbbbb; } + .highlight .mf { + color: #009999; } + .highlight .mh { + color: #009999; } + .highlight .mi { + color: #009999; } + .highlight .mo { + color: #009999; } + .highlight .sb { + color: #d14; } + .highlight .sc { + color: #d14; } + .highlight .sd { + color: #d14; } + .highlight .s2 { + color: #d14; } + .highlight .se { + color: #d14; } + .highlight .sh { + color: #d14; } + .highlight .si { + color: #d14; } + .highlight .sx { + color: #d14; } + .highlight .sr { + color: #009926; } + .highlight .s1 { + color: #d14; } + .highlight .ss { + color: #990073; } + .highlight .bp { + color: #999999; } + .highlight .vc { + color: #008080; } + .highlight .vg { + color: #008080; } + .highlight .vi { + color: #008080; } + .highlight .il { + color: #009999; } diff --git a/docs/1.2.3/AsyncHTTPClient/css/jazzy.css b/docs/1.2.3/AsyncHTTPClient/css/jazzy.css new file mode 100644 index 000000000..ff59f5f86 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/css/jazzy.css @@ -0,0 +1,395 @@ +*, *:before, *:after { + box-sizing: inherit; } + +body { + margin: 0; + background: #fff; + color: #333; + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + letter-spacing: .2px; + -webkit-font-smoothing: antialiased; + box-sizing: border-box; } + +h1 { + font-size: 2rem; + font-weight: 700; + margin: 1.275em 0 0.6em; } + +h2 { + font-size: 1.75rem; + font-weight: 700; + margin: 1.275em 0 0.3em; } + +h3 { + font-size: 1.5rem; + font-weight: 700; + margin: 1em 0 0.3em; } + +h4 { + font-size: 1.25rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h5 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h6 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; + color: #777; } + +p { + margin: 0 0 1em; } + +ul, ol { + padding: 0 0 0 2em; + margin: 0 0 0.85em; } + +blockquote { + margin: 0 0 0.85em; + padding: 0 15px; + color: #858585; + border-left: 4px solid #e5e5e5; } + +img { + max-width: 100%; } + +a { + color: #4183c4; + text-decoration: none; } + a:hover, a:focus { + outline: 0; + text-decoration: underline; } + a.discouraged { + text-decoration: line-through; } + a.discouraged:hover, a.discouraged:focus { + text-decoration: underline line-through; } + +table { + background: #fff; + width: 100%; + border-collapse: collapse; + border-spacing: 0; + overflow: auto; + margin: 0 0 0.85em; } + +tr:nth-child(2n) { + background-color: #fbfbfb; } + +th, td { + padding: 6px 13px; + border: 1px solid #ddd; } + +pre { + margin: 0 0 1.275em; + padding: .85em 1em; + overflow: auto; + background: #f7f7f7; + font-size: .85em; + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +code { + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +.item-container p > code, .item-container li > code, .top-matter p > code, .top-matter li > code { + background: #f7f7f7; + padding: .2em; } + .item-container p > code:before, .item-container p > code:after, .item-container li > code:before, .item-container li > code:after, .top-matter p > code:before, .top-matter p > code:after, .top-matter li > code:before, .top-matter li > code:after { + letter-spacing: -.2em; + content: "\00a0"; } + +pre code { + padding: 0; + white-space: pre; } + +.content-wrapper { + display: flex; + flex-direction: column; } + @media (min-width: 768px) { + .content-wrapper { + flex-direction: row; } } +.header { + display: flex; + padding: 8px; + font-size: 0.875em; + background: #444; + color: #999; } + +.header-col { + margin: 0; + padding: 0 8px; } + +.header-col--primary { + flex: 1; } + +.header-link { + color: #fff; } + +.header-icon { + padding-right: 6px; + vertical-align: -4px; + height: 16px; } + +.breadcrumbs { + font-size: 0.875em; + padding: 8px 16px; + margin: 0; + background: #fbfbfb; + border-bottom: 1px solid #ddd; } + +.carat { + height: 10px; + margin: 0 5px; } + +.navigation { + order: 2; } + @media (min-width: 768px) { + .navigation { + order: 1; + width: 25%; + max-width: 300px; + padding-bottom: 64px; + overflow: hidden; + word-wrap: normal; + background: #fbfbfb; + border-right: 1px solid #ddd; } } +.nav-groups { + list-style-type: none; + padding-left: 0; } + +.nav-group-name { + border-bottom: 1px solid #ddd; + padding: 8px 0 8px 16px; } + +.nav-group-name-link { + color: #333; } + +.nav-group-tasks { + margin: 8px 0; + padding: 0 0 0 8px; } + +.nav-group-task { + font-size: 1em; + list-style-type: none; + white-space: nowrap; } + +.nav-group-task-link { + color: #808080; } + +.main-content { + order: 1; } + @media (min-width: 768px) { + .main-content { + order: 2; + flex: 1; + padding-bottom: 60px; } } +.section { + padding: 0 32px; + border-bottom: 1px solid #ddd; } + +.section-content { + max-width: 834px; + margin: 0 auto; + padding: 16px 0; } + +.section-name { + color: #666; + display: block; } + .section-name p { + margin-bottom: inherit; } + +.declaration .highlight { + overflow-x: initial; + padding: 8px 0; + margin: 0; + background-color: transparent; + border: none; } + +.task-group-section { + border-top: 1px solid #ddd; } + +.task-group { + padding-top: 0px; } + +.task-name-container a[name]:before { + content: ""; + display: block; } + +.section-name-container { + position: relative; } + .section-name-container .section-name-link { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + margin-bottom: 0; } + .section-name-container .section-name { + position: relative; + pointer-events: none; + z-index: 1; } + .section-name-container .section-name a { + pointer-events: auto; } + +.item-container { + padding: 0; } + +.item { + padding-top: 8px; + width: 100%; + list-style-type: none; } + .item a[name]:before { + content: ""; + display: block; } + .item .token, .item .direct-link { + display: inline-block; + text-indent: -20px; + padding-left: 3px; + margin-left: 20px; + font-size: 1rem; } + .item .declaration-note { + font-size: .85em; + color: #808080; + font-style: italic; } + +.pointer-container { + border-bottom: 1px solid #ddd; + left: -23px; + padding-bottom: 13px; + position: relative; + width: 110%; } + +.pointer { + left: 21px; + top: 7px; + display: block; + position: absolute; + width: 12px; + height: 12px; + border-left: 1px solid #ddd; + border-top: 1px solid #ddd; + background: #fff; + transform: rotate(45deg); } + +.height-container { + display: none; + position: relative; + width: 100%; + overflow: hidden; } + .height-container .section { + background: #fff; + border: 1px solid #ddd; + border-top-width: 0; + padding-top: 10px; + padding-bottom: 5px; + padding: 8px 16px; } + +.aside, .language { + padding: 6px 12px; + margin: 12px 0; + border-left: 5px solid #dddddd; + overflow-y: hidden; } + .aside .aside-title, .language .aside-title { + font-size: 9px; + letter-spacing: 2px; + text-transform: uppercase; + padding-bottom: 0; + margin: 0; + color: #aaa; + -webkit-user-select: none; } + .aside p:last-child, .language p:last-child { + margin-bottom: 0; } + +.language { + border-left: 5px solid #cde9f4; } + .language .aside-title { + color: #4183c4; } + +.aside-warning, .aside-deprecated, .aside-unavailable { + border-left: 5px solid #ff6666; } + .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { + color: #ff0000; } + +.graybox { + border-collapse: collapse; + width: 100%; } + .graybox p { + margin: 0; + word-break: break-word; + min-width: 50px; } + .graybox td { + border: 1px solid #ddd; + padding: 5px 25px 5px 10px; + vertical-align: middle; } + .graybox tr td:first-of-type { + text-align: right; + padding: 7px; + vertical-align: top; + word-break: normal; + width: 40px; } + +.slightly-smaller { + font-size: 0.9em; } + +.footer { + padding: 8px 16px; + background: #444; + color: #ddd; + font-size: 0.8em; } + .footer p { + margin: 8px 0; } + .footer a { + color: #fff; } + +html.dash .header, html.dash .breadcrumbs, html.dash .navigation { + display: none; } + +html.dash .height-container { + display: block; } + +form[role=search] input { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 24px; + padding: 0 10px; + margin: 0; + border: none; + border-radius: 1em; } + .loading form[role=search] input { + background: white url(http://github.com/img/spinner.gif) center right 4px no-repeat; } + +form[role=search] .tt-menu { + margin: 0; + min-width: 300px; + background: #fbfbfb; + color: #333; + border: 1px solid #ddd; } + +form[role=search] .tt-highlight { + font-weight: bold; } + +form[role=search] .tt-suggestion { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 0 8px; } + form[role=search] .tt-suggestion span { + display: table-cell; + white-space: nowrap; } + form[role=search] .tt-suggestion .doc-parent-name { + width: 100%; + text-align: right; + font-weight: normal; + font-size: 0.9em; + padding-left: 16px; } + +form[role=search] .tt-suggestion:hover, +form[role=search] .tt-suggestion.tt-cursor { + cursor: pointer; + background-color: #4183c4; + color: #fff; } + +form[role=search] .tt-suggestion:hover .doc-parent-name, +form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { + color: #fff; } diff --git a/docs/1.2.3/AsyncHTTPClient/img/carat.png b/docs/1.2.3/AsyncHTTPClient/img/carat.png new file mode 100755 index 000000000..29d2f7fd4 Binary files /dev/null and b/docs/1.2.3/AsyncHTTPClient/img/carat.png differ diff --git a/docs/1.2.3/AsyncHTTPClient/img/dash.png b/docs/1.2.3/AsyncHTTPClient/img/dash.png new file mode 100755 index 000000000..6f694c7a0 Binary files /dev/null and b/docs/1.2.3/AsyncHTTPClient/img/dash.png differ diff --git a/docs/1.2.3/AsyncHTTPClient/img/gh.png b/docs/1.2.3/AsyncHTTPClient/img/gh.png new file mode 100755 index 000000000..628da97c7 Binary files /dev/null and b/docs/1.2.3/AsyncHTTPClient/img/gh.png differ diff --git a/docs/1.2.3/AsyncHTTPClient/img/spinner.gif b/docs/1.2.3/AsyncHTTPClient/img/spinner.gif new file mode 100644 index 000000000..e3038d0a4 Binary files /dev/null and b/docs/1.2.3/AsyncHTTPClient/img/spinner.gif differ diff --git a/docs/1.2.3/AsyncHTTPClient/index.html b/docs/1.2.3/AsyncHTTPClient/index.html new file mode 100644 index 000000000..bbe6ec7fd --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/index.html @@ -0,0 +1,171 @@ + + + + AsyncHTTPClient Reference + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.2.3 Docs + + (88% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+ +

AsyncHTTPClient Docs

+ +

AsyncHTTPClient is a Swift HTTP Client package.

+ +

To get started with AsyncHTTPClient, import AsyncHTTPClient. The +most important type is HTTPClient +which you can use to emit log messages.

+ +
+
+ + +
+
+ + + + diff --git a/docs/1.2.3/AsyncHTTPClient/js/jazzy.js b/docs/1.2.3/AsyncHTTPClient/js/jazzy.js new file mode 100755 index 000000000..1e55d6ef0 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/js/jazzy.js @@ -0,0 +1,70 @@ +window.jazzy = {'docset': false} +if (typeof window.dash != 'undefined') { + document.documentElement.className += ' dash' + window.jazzy.docset = true +} +if (navigator.userAgent.match(/xcode/i)) { + document.documentElement.className += ' xcode' + window.jazzy.docset = true +} + +function toggleItem($link, $content) { + var animationDuration = 300; + $link.toggleClass('token-open'); + $content.slideToggle(animationDuration); +} + +function itemLinkToContent($link) { + return $link.parent().parent().next(); +} + +// On doc load + hash-change, open any targetted item +function openCurrentItemIfClosed() { + if (window.jazzy.docset) { + return; + } + var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); + $content = itemLinkToContent($link); + if ($content.is(':hidden')) { + toggleItem($link, $content); + } +} + +$(openCurrentItemIfClosed); +$(window).on('hashchange', openCurrentItemIfClosed); + +// On item link ('token') click, toggle its discussion +$('.token').on('click', function(event) { + if (window.jazzy.docset) { + return; + } + var $link = $(this); + toggleItem($link, itemLinkToContent($link)); + + // Keeps the document from jumping to the hash. + var href = $link.attr('href'); + if (history.pushState) { + history.pushState({}, '', href); + } else { + location.hash = href; + } + event.preventDefault(); +}); + +// Clicks on links to the current, closed, item need to open the item +$("a:not('.token')").on('click', function() { + if (location == this.href) { + openCurrentItemIfClosed(); + } +}); + +// KaTeX rendering +if ("katex" in window) { + $($('.math').each( (_, element) => { + katex.render(element.textContent, element, { + displayMode: $(element).hasClass('m-block'), + throwOnError: false, + trust: true + }); + })) +} diff --git a/docs/1.2.3/AsyncHTTPClient/js/jazzy.search.js b/docs/1.2.3/AsyncHTTPClient/js/jazzy.search.js new file mode 100644 index 000000000..e3d1ab905 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/js/jazzy.search.js @@ -0,0 +1,70 @@ +$(function(){ + var $typeahead = $('[data-typeahead]'); + var $form = $typeahead.parents('form'); + var searchURL = $form.attr('action'); + + function displayTemplate(result) { + return result.name; + } + + function suggestionTemplate(result) { + var t = '
'; + t += '' + result.name + ''; + if (result.parent_name) { + t += '' + result.parent_name + ''; + } + t += '
'; + return t; + } + + $typeahead.one('focus', function() { + $form.addClass('loading'); + + $.getJSON(searchURL).then(function(searchData) { + const searchIndex = lunr(function() { + this.ref('url'); + this.field('name'); + this.field('abstract'); + for (const [url, doc] of Object.entries(searchData)) { + this.add({url: url, name: doc.name, abstract: doc.abstract}); + } + }); + + $typeahead.typeahead( + { + highlight: true, + minLength: 3, + autoselect: true + }, + { + limit: 10, + display: displayTemplate, + templates: { suggestion: suggestionTemplate }, + source: function(query, sync) { + const lcSearch = query.toLowerCase(); + const results = searchIndex.query(function(q) { + q.term(lcSearch, { boost: 100 }); + q.term(lcSearch, { + boost: 10, + wildcard: lunr.Query.wildcard.TRAILING + }); + }).map(function(result) { + var doc = searchData[result.ref]; + doc.url = result.ref; + return doc; + }); + sync(results); + } + } + ); + $form.removeClass('loading'); + $typeahead.trigger('focus'); + }); + }); + + var baseURL = searchURL.slice(0, -"search.json".length); + + $typeahead.on('typeahead:select', function(e, result) { + window.location = baseURL + result.url; + }); +}); diff --git a/docs/1.2.3/AsyncHTTPClient/js/jquery.min.js b/docs/1.2.3/AsyncHTTPClient/js/jquery.min.js new file mode 100644 index 000000000..b0614034a --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 00){var c=e.utils.clone(r)||{};c.position=[a,l],c.index=s.length,s.push(new e.Token(i.slice(a,o),c))}a=o+1}}return s},e.tokenizer.separator=/[\s\-]+/,e.Pipeline=function(){this._stack=[]},e.Pipeline.registeredFunctions=Object.create(null),e.Pipeline.registerFunction=function(t,r){r in this.registeredFunctions&&e.utils.warn("Overwriting existing registered function: "+r),t.label=r,e.Pipeline.registeredFunctions[t.label]=t},e.Pipeline.warnIfFunctionNotRegistered=function(t){var r=t.label&&t.label in this.registeredFunctions;r||e.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",t)},e.Pipeline.load=function(t){var r=new e.Pipeline;return t.forEach(function(t){var i=e.Pipeline.registeredFunctions[t];if(!i)throw new Error("Cannot load unregistered function: "+t);r.add(i)}),r},e.Pipeline.prototype.add=function(){var t=Array.prototype.slice.call(arguments);t.forEach(function(t){e.Pipeline.warnIfFunctionNotRegistered(t),this._stack.push(t)},this)},e.Pipeline.prototype.after=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,r)},e.Pipeline.prototype.before=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");this._stack.splice(i,0,r)},e.Pipeline.prototype.remove=function(e){var t=this._stack.indexOf(e);t!=-1&&this._stack.splice(t,1)},e.Pipeline.prototype.run=function(e){for(var t=this._stack.length,r=0;r1&&(se&&(r=n),s!=e);)i=r-t,n=t+Math.floor(i/2),s=this.elements[2*n];return s==e?2*n:s>e?2*n:sa?l+=2:o==a&&(t+=r[u+1]*i[l+1],u+=2,l+=2);return t},e.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},e.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),t=1,r=0;t0){var o,a=s.str.charAt(0);a in s.node.edges?o=s.node.edges[a]:(o=new e.TokenSet,s.node.edges[a]=o),1==s.str.length&&(o["final"]=!0),n.push({node:o,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(0!=s.editsRemaining){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new e.TokenSet;s.node.edges["*"]=u}if(0==s.str.length&&(u["final"]=!0),n.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&n.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),1==s.str.length&&(s.node["final"]=!0),s.str.length>=1){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new e.TokenSet;s.node.edges["*"]=l}1==s.str.length&&(l["final"]=!0),n.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var c,h=s.str.charAt(0),d=s.str.charAt(1);d in s.node.edges?c=s.node.edges[d]:(c=new e.TokenSet,s.node.edges[d]=c),1==s.str.length&&(c["final"]=!0),n.push({node:c,editsRemaining:s.editsRemaining-1,str:h+s.str.slice(2)})}}}return i},e.TokenSet.fromString=function(t){for(var r=new e.TokenSet,i=r,n=0,s=t.length;n=e;t--){var r=this.uncheckedNodes[t],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r["char"]]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}},e.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},e.Index.prototype.search=function(t){return this.query(function(r){var i=new e.QueryParser(t,r);i.parse()})},e.Index.prototype.query=function(t){for(var r=new e.Query(this.fields),i=Object.create(null),n=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),u=0;u1?this._b=1:this._b=e},e.Builder.prototype.k1=function(e){this._k1=e},e.Builder.prototype.add=function(t,r){var i=t[this._ref],n=Object.keys(this._fields);this._documents[i]=r||{},this.documentCount+=1;for(var s=0;s=this.length)return e.QueryLexer.EOS;var t=this.str.charAt(this.pos);return this.pos+=1,t},e.QueryLexer.prototype.width=function(){return this.pos-this.start},e.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},e.QueryLexer.prototype.backup=function(){this.pos-=1},e.QueryLexer.prototype.acceptDigitRun=function(){var t,r;do t=this.next(),r=t.charCodeAt(0);while(r>47&&r<58);t!=e.QueryLexer.EOS&&this.backup()},e.QueryLexer.prototype.more=function(){return this.pos1&&(t.backup(),t.emit(e.QueryLexer.TERM)),t.ignore(),t.more())return e.QueryLexer.lexText},e.QueryLexer.lexEditDistance=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.EDIT_DISTANCE),e.QueryLexer.lexText},e.QueryLexer.lexBoost=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.BOOST),e.QueryLexer.lexText},e.QueryLexer.lexEOS=function(t){t.width()>0&&t.emit(e.QueryLexer.TERM)},e.QueryLexer.termSeparator=e.tokenizer.separator,e.QueryLexer.lexText=function(t){for(;;){var r=t.next();if(r==e.QueryLexer.EOS)return e.QueryLexer.lexEOS;if(92!=r.charCodeAt(0)){if(":"==r)return e.QueryLexer.lexField;if("~"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexEditDistance;if("^"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexBoost;if("+"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if("-"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if(r.match(e.QueryLexer.termSeparator))return e.QueryLexer.lexTerm}else t.escapeCharacter()}},e.QueryParser=function(t,r){this.lexer=new e.QueryLexer(t),this.query=r,this.currentClause={},this.lexemeIdx=0},e.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var t=e.QueryParser.parseClause;t;)t=t(this);return this.query},e.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},e.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},e.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},e.QueryParser.parseClause=function(t){var r=t.peekLexeme();if(void 0!=r)switch(r.type){case e.QueryLexer.PRESENCE:return e.QueryParser.parsePresence;case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expected either a field or a term, found "+r.type;throw r.str.length>=1&&(i+=" with value '"+r.str+"'"),new e.QueryParseError(i,r.start,r.end)}},e.QueryParser.parsePresence=function(t){var r=t.consumeLexeme();if(void 0!=r){switch(r.str){case"-":t.currentClause.presence=e.Query.presence.PROHIBITED;break;case"+":t.currentClause.presence=e.Query.presence.REQUIRED;break;default:var i="unrecognised presence operator'"+r.str+"'";throw new e.QueryParseError(i,r.start,r.end)}var n=t.peekLexeme();if(void 0==n){var i="expecting term or field, found nothing";throw new e.QueryParseError(i,r.start,r.end)}switch(n.type){case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expecting term or field, found '"+n.type+"'";throw new e.QueryParseError(i,n.start,n.end)}}},e.QueryParser.parseField=function(t){var r=t.consumeLexeme();if(void 0!=r){if(t.query.allFields.indexOf(r.str)==-1){var i=t.query.allFields.map(function(e){return"'"+e+"'"}).join(", "),n="unrecognised field '"+r.str+"', possible fields: "+i;throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.fields=[r.str];var s=t.peekLexeme();if(void 0==s){var n="expecting term, found nothing";throw new e.QueryParseError(n,r.start,r.end)}switch(s.type){case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var n="expecting term, found '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseTerm=function(t){var r=t.consumeLexeme();if(void 0!=r){t.currentClause.term=r.str.toLowerCase(),r.str.indexOf("*")!=-1&&(t.currentClause.usePipeline=!1);var i=t.peekLexeme();if(void 0==i)return void t.nextClause();switch(i.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+i.type+"'";throw new e.QueryParseError(n,i.start,i.end)}}},e.QueryParser.parseEditDistance=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="edit distance must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.editDistance=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseBoost=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="boost must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.boost=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():e.lunr=t()}(this,function(){return e})}(); diff --git a/docs/1.2.3/AsyncHTTPClient/js/typeahead.jquery.js b/docs/1.2.3/AsyncHTTPClient/js/typeahead.jquery.js new file mode 100644 index 000000000..3a2d2ab03 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/js/typeahead.jquery.js @@ -0,0 +1,1694 @@ +/*! + * typeahead.js 1.3.1 + * https://github.com/corejavascript/typeahead.js + * Copyright 2013-2020 Twitter, Inc. and other contributors; Licensed MIT + */ + + +(function(root, factory) { + if (typeof define === "function" && define.amd) { + define([ "jquery" ], function(a0) { + return factory(a0); + }); + } else if (typeof module === "object" && module.exports) { + module.exports = factory(require("jquery")); + } else { + factory(root["jQuery"]); + } +})(this, function($) { + var _ = function() { + "use strict"; + return { + isMsie: function() { + return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false; + }, + isBlankString: function(str) { + return !str || /^\s*$/.test(str); + }, + escapeRegExChars: function(str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + }, + isString: function(obj) { + return typeof obj === "string"; + }, + isNumber: function(obj) { + return typeof obj === "number"; + }, + isArray: $.isArray, + isFunction: $.isFunction, + isObject: $.isPlainObject, + isUndefined: function(obj) { + return typeof obj === "undefined"; + }, + isElement: function(obj) { + return !!(obj && obj.nodeType === 1); + }, + isJQuery: function(obj) { + return obj instanceof $; + }, + toStr: function toStr(s) { + return _.isUndefined(s) || s === null ? "" : s + ""; + }, + bind: $.proxy, + each: function(collection, cb) { + $.each(collection, reverseArgs); + function reverseArgs(index, value) { + return cb(value, index); + } + }, + map: $.map, + filter: $.grep, + every: function(obj, test) { + var result = true; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (!(result = test.call(null, val, key, obj))) { + return false; + } + }); + return !!result; + }, + some: function(obj, test) { + var result = false; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (result = test.call(null, val, key, obj)) { + return false; + } + }); + return !!result; + }, + mixin: $.extend, + identity: function(x) { + return x; + }, + clone: function(obj) { + return $.extend(true, {}, obj); + }, + getIdGenerator: function() { + var counter = 0; + return function() { + return counter++; + }; + }, + templatify: function templatify(obj) { + return $.isFunction(obj) ? obj : template; + function template() { + return String(obj); + } + }, + defer: function(fn) { + setTimeout(fn, 0); + }, + debounce: function(func, wait, immediate) { + var timeout, result; + return function() { + var context = this, args = arguments, later, callNow; + later = function() { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + } + }; + callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + } + return result; + }; + }, + throttle: function(func, wait) { + var context, args, timeout, result, previous, later; + previous = 0; + later = function() { + previous = new Date(); + timeout = null; + result = func.apply(context, args); + }; + return function() { + var now = new Date(), remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + } else if (!timeout) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }, + stringify: function(val) { + return _.isString(val) ? val : JSON.stringify(val); + }, + guid: function() { + function _p8(s) { + var p = (Math.random().toString(16) + "000000000").substr(2, 8); + return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p; + } + return "tt-" + _p8() + _p8(true) + _p8(true) + _p8(); + }, + noop: function() {} + }; + }(); + var WWW = function() { + "use strict"; + var defaultClassNames = { + wrapper: "twitter-typeahead", + input: "tt-input", + hint: "tt-hint", + menu: "tt-menu", + dataset: "tt-dataset", + suggestion: "tt-suggestion", + selectable: "tt-selectable", + empty: "tt-empty", + open: "tt-open", + cursor: "tt-cursor", + highlight: "tt-highlight" + }; + return build; + function build(o) { + var www, classes; + classes = _.mixin({}, defaultClassNames, o); + www = { + css: buildCss(), + classes: classes, + html: buildHtml(classes), + selectors: buildSelectors(classes) + }; + return { + css: www.css, + html: www.html, + classes: www.classes, + selectors: www.selectors, + mixin: function(o) { + _.mixin(o, www); + } + }; + } + function buildHtml(c) { + return { + wrapper: '', + menu: '
' + }; + } + function buildSelectors(classes) { + var selectors = {}; + _.each(classes, function(v, k) { + selectors[k] = "." + v; + }); + return selectors; + } + function buildCss() { + var css = { + wrapper: { + position: "relative", + display: "inline-block" + }, + hint: { + position: "absolute", + top: "0", + left: "0", + borderColor: "transparent", + boxShadow: "none", + opacity: "1" + }, + input: { + position: "relative", + verticalAlign: "top", + backgroundColor: "transparent" + }, + inputWithNoHint: { + position: "relative", + verticalAlign: "top" + }, + menu: { + position: "absolute", + top: "100%", + left: "0", + zIndex: "100", + display: "none" + }, + ltr: { + left: "0", + right: "auto" + }, + rtl: { + left: "auto", + right: " 0" + } + }; + if (_.isMsie()) { + _.mixin(css.input, { + backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" + }); + } + return css; + } + }(); + var EventBus = function() { + "use strict"; + var namespace, deprecationMap; + namespace = "typeahead:"; + deprecationMap = { + render: "rendered", + cursorchange: "cursorchanged", + select: "selected", + autocomplete: "autocompleted" + }; + function EventBus(o) { + if (!o || !o.el) { + $.error("EventBus initialized without el"); + } + this.$el = $(o.el); + } + _.mixin(EventBus.prototype, { + _trigger: function(type, args) { + var $e = $.Event(namespace + type); + this.$el.trigger.call(this.$el, $e, args || []); + return $e; + }, + before: function(type) { + var args, $e; + args = [].slice.call(arguments, 1); + $e = this._trigger("before" + type, args); + return $e.isDefaultPrevented(); + }, + trigger: function(type) { + var deprecatedType; + this._trigger(type, [].slice.call(arguments, 1)); + if (deprecatedType = deprecationMap[type]) { + this._trigger(deprecatedType, [].slice.call(arguments, 1)); + } + } + }); + return EventBus; + }(); + var EventEmitter = function() { + "use strict"; + var splitter = /\s+/, nextTick = getNextTick(); + return { + onSync: onSync, + onAsync: onAsync, + off: off, + trigger: trigger + }; + function on(method, types, cb, context) { + var type; + if (!cb) { + return this; + } + types = types.split(splitter); + cb = context ? bindContext(cb, context) : cb; + this._callbacks = this._callbacks || {}; + while (type = types.shift()) { + this._callbacks[type] = this._callbacks[type] || { + sync: [], + async: [] + }; + this._callbacks[type][method].push(cb); + } + return this; + } + function onAsync(types, cb, context) { + return on.call(this, "async", types, cb, context); + } + function onSync(types, cb, context) { + return on.call(this, "sync", types, cb, context); + } + function off(types) { + var type; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + while (type = types.shift()) { + delete this._callbacks[type]; + } + return this; + } + function trigger(types) { + var type, callbacks, args, syncFlush, asyncFlush; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + args = [].slice.call(arguments, 1); + while ((type = types.shift()) && (callbacks = this._callbacks[type])) { + syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args)); + asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args)); + syncFlush() && nextTick(asyncFlush); + } + return this; + } + function getFlush(callbacks, context, args) { + return flush; + function flush() { + var cancelled; + for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) { + cancelled = callbacks[i].apply(context, args) === false; + } + return !cancelled; + } + } + function getNextTick() { + var nextTickFn; + if (window.setImmediate) { + nextTickFn = function nextTickSetImmediate(fn) { + setImmediate(function() { + fn(); + }); + }; + } else { + nextTickFn = function nextTickSetTimeout(fn) { + setTimeout(function() { + fn(); + }, 0); + }; + } + return nextTickFn; + } + function bindContext(fn, context) { + return fn.bind ? fn.bind(context) : function() { + fn.apply(context, [].slice.call(arguments, 0)); + }; + } + }(); + var highlight = function(doc) { + "use strict"; + var defaults = { + node: null, + pattern: null, + tagName: "strong", + className: null, + wordsOnly: false, + caseSensitive: false, + diacriticInsensitive: false + }; + var accented = { + A: "[AaªÀ-Åà-åĀ-ąǍǎȀ-ȃȦȧᴬᵃḀḁẚẠ-ảₐ℀℁℻⒜Ⓐⓐ㍱-㍴㎀-㎄㎈㎉㎩-㎯㏂㏊㏟㏿Aa]", + B: "[BbᴮᵇḂ-ḇℬ⒝Ⓑⓑ㍴㎅-㎇㏃㏈㏔㏝Bb]", + C: "[CcÇçĆ-čᶜ℀ℂ℃℅℆ℭⅭⅽ⒞Ⓒⓒ㍶㎈㎉㎝㎠㎤㏄-㏇Cc]", + D: "[DdĎďDŽ-džDZ-dzᴰᵈḊ-ḓⅅⅆⅮⅾ⒟Ⓓⓓ㋏㍲㍷-㍹㎗㎭-㎯㏅㏈Dd]", + E: "[EeÈ-Ëè-ëĒ-ěȄ-ȇȨȩᴱᵉḘ-ḛẸ-ẽₑ℡ℯℰⅇ⒠Ⓔⓔ㉐㋍㋎Ee]", + F: "[FfᶠḞḟ℉ℱ℻⒡Ⓕⓕ㎊-㎌㎙ff-fflFf]", + G: "[GgĜ-ģǦǧǴǵᴳᵍḠḡℊ⒢Ⓖⓖ㋌㋍㎇㎍-㎏㎓㎬㏆㏉㏒㏿Gg]", + H: "[HhĤĥȞȟʰᴴḢ-ḫẖℋ-ℎ⒣Ⓗⓗ㋌㍱㎐-㎔㏊㏋㏗Hh]", + I: "[IiÌ-Ïì-ïĨ-İIJijǏǐȈ-ȋᴵᵢḬḭỈ-ịⁱℐℑℹⅈⅠ-ⅣⅥ-ⅨⅪⅫⅰ-ⅳⅵ-ⅸⅺⅻ⒤Ⓘⓘ㍺㏌㏕fiffiIi]", + J: "[JjIJ-ĵLJ-njǰʲᴶⅉ⒥ⒿⓙⱼJj]", + K: "[KkĶķǨǩᴷᵏḰ-ḵK⒦Ⓚⓚ㎄㎅㎉㎏㎑㎘㎞㎢㎦㎪㎸㎾㏀㏆㏍-㏏Kk]", + L: "[LlĹ-ŀLJ-ljˡᴸḶḷḺ-ḽℒℓ℡Ⅼⅼ⒧Ⓛⓛ㋏㎈㎉㏐-㏓㏕㏖㏿flfflLl]", + M: "[MmᴹᵐḾ-ṃ℠™ℳⅯⅿ⒨Ⓜⓜ㍷-㍹㎃㎆㎎㎒㎖㎙-㎨㎫㎳㎷㎹㎽㎿㏁㏂㏎㏐㏔-㏖㏘㏙㏞㏟Mm]", + N: "[NnÑñŃ-ʼnNJ-njǸǹᴺṄ-ṋⁿℕ№⒩Ⓝⓝ㎁㎋㎚㎱㎵㎻㏌㏑Nn]", + O: "[OoºÒ-Öò-öŌ-őƠơǑǒǪǫȌ-ȏȮȯᴼᵒỌ-ỏₒ℅№ℴ⒪Ⓞⓞ㍵㏇㏒㏖Oo]", + P: "[PpᴾᵖṔ-ṗℙ⒫Ⓟⓟ㉐㍱㍶㎀㎊㎩-㎬㎰㎴㎺㏋㏗-㏚Pp]", + Q: "[Qqℚ⒬Ⓠⓠ㏃Qq]", + R: "[RrŔ-řȐ-ȓʳᴿᵣṘ-ṛṞṟ₨ℛ-ℝ⒭Ⓡⓡ㋍㍴㎭-㎯㏚㏛Rr]", + S: "[SsŚ-šſȘșˢṠ-ṣ₨℁℠⒮Ⓢⓢ㎧㎨㎮-㎳㏛㏜stSs]", + T: "[TtŢ-ťȚțᵀᵗṪ-ṱẗ℡™⒯Ⓣⓣ㉐㋏㎔㏏ſtstTt]", + U: "[UuÙ-Üù-üŨ-ųƯưǓǔȔ-ȗᵁᵘᵤṲ-ṷỤ-ủ℆⒰Ⓤⓤ㍳㍺Uu]", + V: "[VvᵛᵥṼ-ṿⅣ-Ⅷⅳ-ⅷ⒱Ⓥⓥⱽ㋎㍵㎴-㎹㏜㏞Vv]", + W: "[WwŴŵʷᵂẀ-ẉẘ⒲Ⓦⓦ㎺-㎿㏝Ww]", + X: "[XxˣẊ-ẍₓ℻Ⅸ-Ⅻⅸ-ⅻ⒳Ⓧⓧ㏓Xx]", + Y: "[YyÝýÿŶ-ŸȲȳʸẎẏẙỲ-ỹ⒴Ⓨⓨ㏉Yy]", + Z: "[ZzŹ-žDZ-dzᶻẐ-ẕℤℨ⒵Ⓩⓩ㎐-㎔Zz]" + }; + return function hightlight(o) { + var regex; + o = _.mixin({}, defaults, o); + if (!o.node || !o.pattern) { + return; + } + o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; + regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly, o.diacriticInsensitive); + traverse(o.node, hightlightTextNode); + function hightlightTextNode(textNode) { + var match, patternNode, wrapperNode; + if (match = regex.exec(textNode.data)) { + wrapperNode = doc.createElement(o.tagName); + o.className && (wrapperNode.className = o.className); + patternNode = textNode.splitText(match.index); + patternNode.splitText(match[0].length); + wrapperNode.appendChild(patternNode.cloneNode(true)); + textNode.parentNode.replaceChild(wrapperNode, patternNode); + } + return !!match; + } + function traverse(el, hightlightTextNode) { + var childNode, TEXT_NODE_TYPE = 3; + for (var i = 0; i < el.childNodes.length; i++) { + childNode = el.childNodes[i]; + if (childNode.nodeType === TEXT_NODE_TYPE) { + i += hightlightTextNode(childNode) ? 1 : 0; + } else { + traverse(childNode, hightlightTextNode); + } + } + } + }; + function accent_replacer(chr) { + return accented[chr.toUpperCase()] || chr; + } + function getRegex(patterns, caseSensitive, wordsOnly, diacriticInsensitive) { + var escapedPatterns = [], regexStr; + for (var i = 0, len = patterns.length; i < len; i++) { + var escapedWord = _.escapeRegExChars(patterns[i]); + if (diacriticInsensitive) { + escapedWord = escapedWord.replace(/\S/g, accent_replacer); + } + escapedPatterns.push(escapedWord); + } + regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")"; + return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); + } + }(window.document); + var Input = function() { + "use strict"; + var specialKeyCodeMap; + specialKeyCodeMap = { + 9: "tab", + 27: "esc", + 37: "left", + 39: "right", + 13: "enter", + 38: "up", + 40: "down" + }; + function Input(o, www) { + var id; + o = o || {}; + if (!o.input) { + $.error("input is missing"); + } + www.mixin(this); + this.$hint = $(o.hint); + this.$input = $(o.input); + this.$menu = $(o.menu); + id = this.$input.attr("id") || _.guid(); + this.$menu.attr("id", id + "_listbox"); + this.$hint.attr({ + "aria-hidden": true + }); + this.$input.attr({ + "aria-owns": id + "_listbox", + role: "combobox", + "aria-autocomplete": "list", + "aria-expanded": false + }); + this.query = this.$input.val(); + this.queryWhenFocused = this.hasFocus() ? this.query : null; + this.$overflowHelper = buildOverflowHelper(this.$input); + this._checkLanguageDirection(); + if (this.$hint.length === 0) { + this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; + } + this.onSync("cursorchange", this._updateDescendent); + } + Input.normalizeQuery = function(str) { + return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " "); + }; + _.mixin(Input.prototype, EventEmitter, { + _onBlur: function onBlur() { + this.resetInputValue(); + this.trigger("blurred"); + }, + _onFocus: function onFocus() { + this.queryWhenFocused = this.query; + this.trigger("focused"); + }, + _onKeydown: function onKeydown($e) { + var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; + this._managePreventDefault(keyName, $e); + if (keyName && this._shouldTrigger(keyName, $e)) { + this.trigger(keyName + "Keyed", $e); + } + }, + _onInput: function onInput() { + this._setQuery(this.getInputValue()); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + _managePreventDefault: function managePreventDefault(keyName, $e) { + var preventDefault; + switch (keyName) { + case "up": + case "down": + preventDefault = !withModifier($e); + break; + + default: + preventDefault = false; + } + preventDefault && $e.preventDefault(); + }, + _shouldTrigger: function shouldTrigger(keyName, $e) { + var trigger; + switch (keyName) { + case "tab": + trigger = !withModifier($e); + break; + + default: + trigger = true; + } + return trigger; + }, + _checkLanguageDirection: function checkLanguageDirection() { + var dir = (this.$input.css("direction") || "ltr").toLowerCase(); + if (this.dir !== dir) { + this.dir = dir; + this.$hint.attr("dir", dir); + this.trigger("langDirChanged", dir); + } + }, + _setQuery: function setQuery(val, silent) { + var areEquivalent, hasDifferentWhitespace; + areEquivalent = areQueriesEquivalent(val, this.query); + hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false; + this.query = val; + if (!silent && !areEquivalent) { + this.trigger("queryChanged", this.query); + } else if (!silent && hasDifferentWhitespace) { + this.trigger("whitespaceChanged", this.query); + } + }, + _updateDescendent: function updateDescendent(event, id) { + this.$input.attr("aria-activedescendant", id); + }, + bind: function() { + var that = this, onBlur, onFocus, onKeydown, onInput; + onBlur = _.bind(this._onBlur, this); + onFocus = _.bind(this._onFocus, this); + onKeydown = _.bind(this._onKeydown, this); + onInput = _.bind(this._onInput, this); + this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown); + if (!_.isMsie() || _.isMsie() > 9) { + this.$input.on("input.tt", onInput); + } else { + this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { + if (specialKeyCodeMap[$e.which || $e.keyCode]) { + return; + } + _.defer(_.bind(that._onInput, that, $e)); + }); + } + return this; + }, + focus: function focus() { + this.$input.focus(); + }, + blur: function blur() { + this.$input.blur(); + }, + getLangDir: function getLangDir() { + return this.dir; + }, + getQuery: function getQuery() { + return this.query || ""; + }, + setQuery: function setQuery(val, silent) { + this.setInputValue(val); + this._setQuery(val, silent); + }, + hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() { + return this.query !== this.queryWhenFocused; + }, + getInputValue: function getInputValue() { + return this.$input.val(); + }, + setInputValue: function setInputValue(value) { + this.$input.val(value); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + resetInputValue: function resetInputValue() { + this.setInputValue(this.query); + }, + getHint: function getHint() { + return this.$hint.val(); + }, + setHint: function setHint(value) { + this.$hint.val(value); + }, + clearHint: function clearHint() { + this.setHint(""); + }, + clearHintIfInvalid: function clearHintIfInvalid() { + var val, hint, valIsPrefixOfHint, isValid; + val = this.getInputValue(); + hint = this.getHint(); + valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; + isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow(); + !isValid && this.clearHint(); + }, + hasFocus: function hasFocus() { + return this.$input.is(":focus"); + }, + hasOverflow: function hasOverflow() { + var constraint = this.$input.width() - 2; + this.$overflowHelper.text(this.getInputValue()); + return this.$overflowHelper.width() >= constraint; + }, + isCursorAtEnd: function() { + var valueLength, selectionStart, range; + valueLength = this.$input.val().length; + selectionStart = this.$input[0].selectionStart; + if (_.isNumber(selectionStart)) { + return selectionStart === valueLength; + } else if (document.selection) { + range = document.selection.createRange(); + range.moveStart("character", -valueLength); + return valueLength === range.text.length; + } + return true; + }, + destroy: function destroy() { + this.$hint.off(".tt"); + this.$input.off(".tt"); + this.$overflowHelper.remove(); + this.$hint = this.$input = this.$overflowHelper = $("
"); + }, + setAriaExpanded: function setAriaExpanded(value) { + this.$input.attr("aria-expanded", value); + } + }); + return Input; + function buildOverflowHelper($input) { + return $('').css({ + position: "absolute", + visibility: "hidden", + whiteSpace: "pre", + fontFamily: $input.css("font-family"), + fontSize: $input.css("font-size"), + fontStyle: $input.css("font-style"), + fontVariant: $input.css("font-variant"), + fontWeight: $input.css("font-weight"), + wordSpacing: $input.css("word-spacing"), + letterSpacing: $input.css("letter-spacing"), + textIndent: $input.css("text-indent"), + textRendering: $input.css("text-rendering"), + textTransform: $input.css("text-transform") + }).insertAfter($input); + } + function areQueriesEquivalent(a, b) { + return Input.normalizeQuery(a) === Input.normalizeQuery(b); + } + function withModifier($e) { + return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; + } + }(); + var Dataset = function() { + "use strict"; + var keys, nameGenerator; + keys = { + dataset: "tt-selectable-dataset", + val: "tt-selectable-display", + obj: "tt-selectable-object" + }; + nameGenerator = _.getIdGenerator(); + function Dataset(o, www) { + o = o || {}; + o.templates = o.templates || {}; + o.templates.notFound = o.templates.notFound || o.templates.empty; + if (!o.source) { + $.error("missing source"); + } + if (!o.node) { + $.error("missing node"); + } + if (o.name && !isValidName(o.name)) { + $.error("invalid dataset name: " + o.name); + } + www.mixin(this); + this.highlight = !!o.highlight; + this.name = _.toStr(o.name || nameGenerator()); + this.limit = o.limit || 5; + this.displayFn = getDisplayFn(o.display || o.displayKey); + this.templates = getTemplates(o.templates, this.displayFn); + this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source; + this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async; + this._resetLastSuggestion(); + this.$el = $(o.node).attr("role", "presentation").addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); + } + Dataset.extractData = function extractData(el) { + var $el = $(el); + if ($el.data(keys.obj)) { + return { + dataset: $el.data(keys.dataset) || "", + val: $el.data(keys.val) || "", + obj: $el.data(keys.obj) || null + }; + } + return null; + }; + _.mixin(Dataset.prototype, EventEmitter, { + _overwrite: function overwrite(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (this.async && this.templates.pending) { + this._renderPending(query); + } else if (!this.async && this.templates.notFound) { + this._renderNotFound(query); + } else { + this._empty(); + } + this.trigger("rendered", suggestions, false, this.name); + }, + _append: function append(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length && this.$lastSuggestion.length) { + this._appendSuggestions(query, suggestions); + } else if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (!this.$lastSuggestion.length && this.templates.notFound) { + this._renderNotFound(query); + } + this.trigger("rendered", suggestions, true, this.name); + }, + _renderSuggestions: function renderSuggestions(query, suggestions) { + var $fragment; + $fragment = this._getSuggestionsFragment(query, suggestions); + this.$lastSuggestion = $fragment.children().last(); + this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions)); + }, + _appendSuggestions: function appendSuggestions(query, suggestions) { + var $fragment, $lastSuggestion; + $fragment = this._getSuggestionsFragment(query, suggestions); + $lastSuggestion = $fragment.children().last(); + this.$lastSuggestion.after($fragment); + this.$lastSuggestion = $lastSuggestion; + }, + _renderPending: function renderPending(query) { + var template = this.templates.pending; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _renderNotFound: function renderNotFound(query) { + var template = this.templates.notFound; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _empty: function empty() { + this.$el.empty(); + this._resetLastSuggestion(); + }, + _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) { + var that = this, fragment; + fragment = document.createDocumentFragment(); + _.each(suggestions, function getSuggestionNode(suggestion) { + var $el, context; + context = that._injectQuery(query, suggestion); + $el = $(that.templates.suggestion(context)).data(keys.dataset, that.name).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable); + fragment.appendChild($el[0]); + }); + this.highlight && highlight({ + className: this.classes.highlight, + node: fragment, + pattern: query + }); + return $(fragment); + }, + _getFooter: function getFooter(query, suggestions) { + return this.templates.footer ? this.templates.footer({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _getHeader: function getHeader(query, suggestions) { + return this.templates.header ? this.templates.header({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _resetLastSuggestion: function resetLastSuggestion() { + this.$lastSuggestion = $(); + }, + _injectQuery: function injectQuery(query, obj) { + return _.isObject(obj) ? _.mixin({ + _query: query + }, obj) : obj; + }, + update: function update(query) { + var that = this, canceled = false, syncCalled = false, rendered = 0; + this.cancel(); + this.cancel = function cancel() { + canceled = true; + that.cancel = $.noop; + that.async && that.trigger("asyncCanceled", query, that.name); + }; + this.source(query, sync, async); + !syncCalled && sync([]); + function sync(suggestions) { + if (syncCalled) { + return; + } + syncCalled = true; + suggestions = (suggestions || []).slice(0, that.limit); + rendered = suggestions.length; + that._overwrite(query, suggestions); + if (rendered < that.limit && that.async) { + that.trigger("asyncRequested", query, that.name); + } + } + function async(suggestions) { + suggestions = suggestions || []; + if (!canceled && rendered < that.limit) { + that.cancel = $.noop; + var idx = Math.abs(rendered - that.limit); + rendered += idx; + that._append(query, suggestions.slice(0, idx)); + that.async && that.trigger("asyncReceived", query, that.name); + } + } + }, + cancel: $.noop, + clear: function clear() { + this._empty(); + this.cancel(); + this.trigger("cleared"); + }, + isEmpty: function isEmpty() { + return this.$el.is(":empty"); + }, + destroy: function destroy() { + this.$el = $("
"); + } + }); + return Dataset; + function getDisplayFn(display) { + display = display || _.stringify; + return _.isFunction(display) ? display : displayFn; + function displayFn(obj) { + return obj[display]; + } + } + function getTemplates(templates, displayFn) { + return { + notFound: templates.notFound && _.templatify(templates.notFound), + pending: templates.pending && _.templatify(templates.pending), + header: templates.header && _.templatify(templates.header), + footer: templates.footer && _.templatify(templates.footer), + suggestion: templates.suggestion ? userSuggestionTemplate : suggestionTemplate + }; + function userSuggestionTemplate(context) { + var template = templates.suggestion; + return $(template(context)).attr("id", _.guid()); + } + function suggestionTemplate(context) { + return $('
').attr("id", _.guid()).text(displayFn(context)); + } + } + function isValidName(str) { + return /^[_a-zA-Z0-9-]+$/.test(str); + } + }(); + var Menu = function() { + "use strict"; + function Menu(o, www) { + var that = this; + o = o || {}; + if (!o.node) { + $.error("node is required"); + } + www.mixin(this); + this.$node = $(o.node); + this.query = null; + this.datasets = _.map(o.datasets, initializeDataset); + function initializeDataset(oDataset) { + var node = that.$node.find(oDataset.node).first(); + oDataset.node = node.length ? node : $("
").appendTo(that.$node); + return new Dataset(oDataset, www); + } + } + _.mixin(Menu.prototype, EventEmitter, { + _onSelectableClick: function onSelectableClick($e) { + this.trigger("selectableClicked", $($e.currentTarget)); + }, + _onRendered: function onRendered(type, dataset, suggestions, async) { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetRendered", dataset, suggestions, async); + }, + _onCleared: function onCleared() { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetCleared"); + }, + _propagate: function propagate() { + this.trigger.apply(this, arguments); + }, + _allDatasetsEmpty: function allDatasetsEmpty() { + return _.every(this.datasets, _.bind(function isDatasetEmpty(dataset) { + var isEmpty = dataset.isEmpty(); + this.$node.attr("aria-expanded", !isEmpty); + return isEmpty; + }, this)); + }, + _getSelectables: function getSelectables() { + return this.$node.find(this.selectors.selectable); + }, + _removeCursor: function _removeCursor() { + var $selectable = this.getActiveSelectable(); + $selectable && $selectable.removeClass(this.classes.cursor); + }, + _ensureVisible: function ensureVisible($el) { + var elTop, elBottom, nodeScrollTop, nodeHeight; + elTop = $el.position().top; + elBottom = elTop + $el.outerHeight(true); + nodeScrollTop = this.$node.scrollTop(); + nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10); + if (elTop < 0) { + this.$node.scrollTop(nodeScrollTop + elTop); + } else if (nodeHeight < elBottom) { + this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight)); + } + }, + bind: function() { + var that = this, onSelectableClick; + onSelectableClick = _.bind(this._onSelectableClick, this); + this.$node.on("click.tt", this.selectors.selectable, onSelectableClick); + this.$node.on("mouseover", this.selectors.selectable, function() { + that.setCursor($(this)); + }); + this.$node.on("mouseleave", function() { + that._removeCursor(); + }); + _.each(this.datasets, function(dataset) { + dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that); + }); + return this; + }, + isOpen: function isOpen() { + return this.$node.hasClass(this.classes.open); + }, + open: function open() { + this.$node.scrollTop(0); + this.$node.addClass(this.classes.open); + }, + close: function close() { + this.$node.attr("aria-expanded", false); + this.$node.removeClass(this.classes.open); + this._removeCursor(); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.attr("dir", dir); + }, + selectableRelativeToCursor: function selectableRelativeToCursor(delta) { + var $selectables, $oldCursor, oldIndex, newIndex; + $oldCursor = this.getActiveSelectable(); + $selectables = this._getSelectables(); + oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1; + newIndex = oldIndex + delta; + newIndex = (newIndex + 1) % ($selectables.length + 1) - 1; + newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex; + return newIndex === -1 ? null : $selectables.eq(newIndex); + }, + setCursor: function setCursor($selectable) { + this._removeCursor(); + if ($selectable = $selectable && $selectable.first()) { + $selectable.addClass(this.classes.cursor); + this._ensureVisible($selectable); + } + }, + getSelectableData: function getSelectableData($el) { + return $el && $el.length ? Dataset.extractData($el) : null; + }, + getActiveSelectable: function getActiveSelectable() { + var $selectable = this._getSelectables().filter(this.selectors.cursor).first(); + return $selectable.length ? $selectable : null; + }, + getTopSelectable: function getTopSelectable() { + var $selectable = this._getSelectables().first(); + return $selectable.length ? $selectable : null; + }, + update: function update(query) { + var isValidUpdate = query !== this.query; + if (isValidUpdate) { + this.query = query; + _.each(this.datasets, updateDataset); + } + return isValidUpdate; + function updateDataset(dataset) { + dataset.update(query); + } + }, + empty: function empty() { + _.each(this.datasets, clearDataset); + this.query = null; + this.$node.addClass(this.classes.empty); + function clearDataset(dataset) { + dataset.clear(); + } + }, + destroy: function destroy() { + this.$node.off(".tt"); + this.$node = $("
"); + _.each(this.datasets, destroyDataset); + function destroyDataset(dataset) { + dataset.destroy(); + } + } + }); + return Menu; + }(); + var Status = function() { + "use strict"; + function Status(options) { + this.$el = $("", { + role: "status", + "aria-live": "polite" + }).css({ + position: "absolute", + padding: "0", + border: "0", + height: "1px", + width: "1px", + "margin-bottom": "-1px", + "margin-right": "-1px", + overflow: "hidden", + clip: "rect(0 0 0 0)", + "white-space": "nowrap" + }); + options.$input.after(this.$el); + _.each(options.menu.datasets, _.bind(function(dataset) { + if (dataset.onSync) { + dataset.onSync("rendered", _.bind(this.update, this)); + dataset.onSync("cleared", _.bind(this.cleared, this)); + } + }, this)); + } + _.mixin(Status.prototype, { + update: function update(event, suggestions) { + var length = suggestions.length; + var words; + if (length === 1) { + words = { + result: "result", + is: "is" + }; + } else { + words = { + result: "results", + is: "are" + }; + } + this.$el.text(length + " " + words.result + " " + words.is + " available, use up and down arrow keys to navigate."); + }, + cleared: function() { + this.$el.text(""); + } + }); + return Status; + }(); + var DefaultMenu = function() { + "use strict"; + var s = Menu.prototype; + function DefaultMenu() { + Menu.apply(this, [].slice.call(arguments, 0)); + } + _.mixin(DefaultMenu.prototype, Menu.prototype, { + open: function open() { + !this._allDatasetsEmpty() && this._show(); + return s.open.apply(this, [].slice.call(arguments, 0)); + }, + close: function close() { + this._hide(); + return s.close.apply(this, [].slice.call(arguments, 0)); + }, + _onRendered: function onRendered() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onRendered.apply(this, [].slice.call(arguments, 0)); + }, + _onCleared: function onCleared() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onCleared.apply(this, [].slice.call(arguments, 0)); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl); + return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0)); + }, + _hide: function hide() { + this.$node.hide(); + }, + _show: function show() { + this.$node.css("display", "block"); + } + }); + return DefaultMenu; + }(); + var Typeahead = function() { + "use strict"; + function Typeahead(o, www) { + var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged; + o = o || {}; + if (!o.input) { + $.error("missing input"); + } + if (!o.menu) { + $.error("missing menu"); + } + if (!o.eventBus) { + $.error("missing event bus"); + } + www.mixin(this); + this.eventBus = o.eventBus; + this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; + this.input = o.input; + this.menu = o.menu; + this.enabled = true; + this.autoselect = !!o.autoselect; + this.active = false; + this.input.hasFocus() && this.activate(); + this.dir = this.input.getLangDir(); + this._hacks(); + this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this); + onFocused = c(this, "activate", "open", "_onFocused"); + onBlurred = c(this, "deactivate", "_onBlurred"); + onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed"); + onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed"); + onEscKeyed = c(this, "isActive", "_onEscKeyed"); + onUpKeyed = c(this, "isActive", "open", "_onUpKeyed"); + onDownKeyed = c(this, "isActive", "open", "_onDownKeyed"); + onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed"); + onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed"); + onQueryChanged = c(this, "_openIfActive", "_onQueryChanged"); + onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged"); + this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this); + } + _.mixin(Typeahead.prototype, { + _hacks: function hacks() { + var $input, $menu; + $input = this.input.$input || $("
"); + $menu = this.menu.$node || $("
"); + $input.on("blur.tt", function($e) { + var active, isActive, hasActive; + active = document.activeElement; + isActive = $menu.is(active); + hasActive = $menu.has(active).length > 0; + if (_.isMsie() && (isActive || hasActive)) { + $e.preventDefault(); + $e.stopImmediatePropagation(); + _.defer(function() { + $input.focus(); + }); + } + }); + $menu.on("mousedown.tt", function($e) { + $e.preventDefault(); + }); + }, + _onSelectableClicked: function onSelectableClicked(type, $el) { + this.select($el); + }, + _onDatasetCleared: function onDatasetCleared() { + this._updateHint(); + }, + _onDatasetRendered: function onDatasetRendered(type, suggestions, async, dataset) { + this._updateHint(); + if (this.autoselect) { + var cursorClass = this.selectors.cursor.substr(1); + this.menu.$node.find(this.selectors.suggestion).first().addClass(cursorClass); + } + this.eventBus.trigger("render", suggestions, async, dataset); + }, + _onAsyncRequested: function onAsyncRequested(type, dataset, query) { + this.eventBus.trigger("asyncrequest", query, dataset); + }, + _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) { + this.eventBus.trigger("asynccancel", query, dataset); + }, + _onAsyncReceived: function onAsyncReceived(type, dataset, query) { + this.eventBus.trigger("asyncreceive", query, dataset); + }, + _onFocused: function onFocused() { + this._minLengthMet() && this.menu.update(this.input.getQuery()); + }, + _onBlurred: function onBlurred() { + if (this.input.hasQueryChangedSinceLastFocus()) { + this.eventBus.trigger("change", this.input.getQuery()); + } + }, + _onEnterKeyed: function onEnterKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + if (this.select($selectable)) { + $e.preventDefault(); + $e.stopPropagation(); + } + } else if (this.autoselect) { + if (this.select(this.menu.getTopSelectable())) { + $e.preventDefault(); + $e.stopPropagation(); + } + } + }, + _onTabKeyed: function onTabKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + this.select($selectable) && $e.preventDefault(); + } else if (this.autoselect) { + if ($selectable = this.menu.getTopSelectable()) { + this.autocomplete($selectable) && $e.preventDefault(); + } + } + }, + _onEscKeyed: function onEscKeyed() { + this.close(); + }, + _onUpKeyed: function onUpKeyed() { + this.moveCursor(-1); + }, + _onDownKeyed: function onDownKeyed() { + this.moveCursor(+1); + }, + _onLeftKeyed: function onLeftKeyed() { + if (this.dir === "rtl" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); + } + }, + _onRightKeyed: function onRightKeyed() { + if (this.dir === "ltr" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); + } + }, + _onQueryChanged: function onQueryChanged(e, query) { + this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty(); + }, + _onWhitespaceChanged: function onWhitespaceChanged() { + this._updateHint(); + }, + _onLangDirChanged: function onLangDirChanged(e, dir) { + if (this.dir !== dir) { + this.dir = dir; + this.menu.setLanguageDirection(dir); + } + }, + _openIfActive: function openIfActive() { + this.isActive() && this.open(); + }, + _minLengthMet: function minLengthMet(query) { + query = _.isString(query) ? query : this.input.getQuery() || ""; + return query.length >= this.minLength; + }, + _updateHint: function updateHint() { + var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match; + $selectable = this.menu.getTopSelectable(); + data = this.menu.getSelectableData($selectable); + val = this.input.getInputValue(); + if (data && !_.isBlankString(val) && !this.input.hasOverflow()) { + query = Input.normalizeQuery(val); + escapedQuery = _.escapeRegExChars(query); + frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i"); + match = frontMatchRegEx.exec(data.val); + match && this.input.setHint(val + match[1]); + } else { + this.input.clearHint(); + } + }, + isEnabled: function isEnabled() { + return this.enabled; + }, + enable: function enable() { + this.enabled = true; + }, + disable: function disable() { + this.enabled = false; + }, + isActive: function isActive() { + return this.active; + }, + activate: function activate() { + if (this.isActive()) { + return true; + } else if (!this.isEnabled() || this.eventBus.before("active")) { + return false; + } else { + this.active = true; + this.eventBus.trigger("active"); + return true; + } + }, + deactivate: function deactivate() { + if (!this.isActive()) { + return true; + } else if (this.eventBus.before("idle")) { + return false; + } else { + this.active = false; + this.close(); + this.eventBus.trigger("idle"); + return true; + } + }, + isOpen: function isOpen() { + return this.menu.isOpen(); + }, + open: function open() { + if (!this.isOpen() && !this.eventBus.before("open")) { + this.input.setAriaExpanded(true); + this.menu.open(); + this._updateHint(); + this.eventBus.trigger("open"); + } + return this.isOpen(); + }, + close: function close() { + if (this.isOpen() && !this.eventBus.before("close")) { + this.input.setAriaExpanded(false); + this.menu.close(); + this.input.clearHint(); + this.input.resetInputValue(); + this.eventBus.trigger("close"); + } + return !this.isOpen(); + }, + setVal: function setVal(val) { + this.input.setQuery(_.toStr(val)); + }, + getVal: function getVal() { + return this.input.getQuery(); + }, + select: function select($selectable) { + var data = this.menu.getSelectableData($selectable); + if (data && !this.eventBus.before("select", data.obj, data.dataset)) { + this.input.setQuery(data.val, true); + this.eventBus.trigger("select", data.obj, data.dataset); + this.close(); + return true; + } + return false; + }, + autocomplete: function autocomplete($selectable) { + var query, data, isValid; + query = this.input.getQuery(); + data = this.menu.getSelectableData($selectable); + isValid = data && query !== data.val; + if (isValid && !this.eventBus.before("autocomplete", data.obj, data.dataset)) { + this.input.setQuery(data.val); + this.eventBus.trigger("autocomplete", data.obj, data.dataset); + return true; + } + return false; + }, + moveCursor: function moveCursor(delta) { + var query, $candidate, data, suggestion, datasetName, cancelMove, id; + query = this.input.getQuery(); + $candidate = this.menu.selectableRelativeToCursor(delta); + data = this.menu.getSelectableData($candidate); + suggestion = data ? data.obj : null; + datasetName = data ? data.dataset : null; + id = $candidate ? $candidate.attr("id") : null; + this.input.trigger("cursorchange", id); + cancelMove = this._minLengthMet() && this.menu.update(query); + if (!cancelMove && !this.eventBus.before("cursorchange", suggestion, datasetName)) { + this.menu.setCursor($candidate); + if (data) { + if (typeof data.val === "string") { + this.input.setInputValue(data.val); + } + } else { + this.input.resetInputValue(); + this._updateHint(); + } + this.eventBus.trigger("cursorchange", suggestion, datasetName); + return true; + } + return false; + }, + destroy: function destroy() { + this.input.destroy(); + this.menu.destroy(); + } + }); + return Typeahead; + function c(ctx) { + var methods = [].slice.call(arguments, 1); + return function() { + var args = [].slice.call(arguments); + _.each(methods, function(method) { + return ctx[method].apply(ctx, args); + }); + }; + } + }(); + (function() { + "use strict"; + var old, keys, methods; + old = $.fn.typeahead; + keys = { + www: "tt-www", + attrs: "tt-attrs", + typeahead: "tt-typeahead" + }; + methods = { + initialize: function initialize(o, datasets) { + var www; + datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); + o = o || {}; + www = WWW(o.classNames); + return this.each(attach); + function attach() { + var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, status, typeahead, MenuConstructor; + _.each(datasets, function(d) { + d.highlight = !!o.highlight; + }); + $input = $(this); + $wrapper = $(www.html.wrapper); + $hint = $elOrNull(o.hint); + $menu = $elOrNull(o.menu); + defaultHint = o.hint !== false && !$hint; + defaultMenu = o.menu !== false && !$menu; + defaultHint && ($hint = buildHintFromInput($input, www)); + defaultMenu && ($menu = $(www.html.menu).css(www.css.menu)); + $hint && $hint.val(""); + $input = prepInput($input, www); + if (defaultHint || defaultMenu) { + $wrapper.css(www.css.wrapper); + $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint); + $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null); + } + MenuConstructor = defaultMenu ? DefaultMenu : Menu; + eventBus = new EventBus({ + el: $input + }); + input = new Input({ + hint: $hint, + input: $input, + menu: $menu + }, www); + menu = new MenuConstructor({ + node: $menu, + datasets: datasets + }, www); + status = new Status({ + $input: $input, + menu: menu + }); + typeahead = new Typeahead({ + input: input, + menu: menu, + eventBus: eventBus, + minLength: o.minLength, + autoselect: o.autoselect + }, www); + $input.data(keys.www, www); + $input.data(keys.typeahead, typeahead); + } + }, + isEnabled: function isEnabled() { + var enabled; + ttEach(this.first(), function(t) { + enabled = t.isEnabled(); + }); + return enabled; + }, + enable: function enable() { + ttEach(this, function(t) { + t.enable(); + }); + return this; + }, + disable: function disable() { + ttEach(this, function(t) { + t.disable(); + }); + return this; + }, + isActive: function isActive() { + var active; + ttEach(this.first(), function(t) { + active = t.isActive(); + }); + return active; + }, + activate: function activate() { + ttEach(this, function(t) { + t.activate(); + }); + return this; + }, + deactivate: function deactivate() { + ttEach(this, function(t) { + t.deactivate(); + }); + return this; + }, + isOpen: function isOpen() { + var open; + ttEach(this.first(), function(t) { + open = t.isOpen(); + }); + return open; + }, + open: function open() { + ttEach(this, function(t) { + t.open(); + }); + return this; + }, + close: function close() { + ttEach(this, function(t) { + t.close(); + }); + return this; + }, + select: function select(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.select($el); + }); + return success; + }, + autocomplete: function autocomplete(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.autocomplete($el); + }); + return success; + }, + moveCursor: function moveCursoe(delta) { + var success = false; + ttEach(this.first(), function(t) { + success = t.moveCursor(delta); + }); + return success; + }, + val: function val(newVal) { + var query; + if (!arguments.length) { + ttEach(this.first(), function(t) { + query = t.getVal(); + }); + return query; + } else { + ttEach(this, function(t) { + t.setVal(_.toStr(newVal)); + }); + return this; + } + }, + destroy: function destroy() { + ttEach(this, function(typeahead, $input) { + revert($input); + typeahead.destroy(); + }); + return this; + } + }; + $.fn.typeahead = function(method) { + if (methods[method]) { + return methods[method].apply(this, [].slice.call(arguments, 1)); + } else { + return methods.initialize.apply(this, arguments); + } + }; + $.fn.typeahead.noConflict = function noConflict() { + $.fn.typeahead = old; + return this; + }; + function ttEach($els, fn) { + $els.each(function() { + var $input = $(this), typeahead; + (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input); + }); + } + function buildHintFromInput($input, www) { + return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop({ + readonly: true, + required: false + }).removeAttr("id name placeholder").removeClass("required").attr({ + spellcheck: "false", + tabindex: -1 + }); + } + function prepInput($input, www) { + $input.data(keys.attrs, { + dir: $input.attr("dir"), + autocomplete: $input.attr("autocomplete"), + spellcheck: $input.attr("spellcheck"), + style: $input.attr("style") + }); + $input.addClass(www.classes.input).attr({ + spellcheck: false + }); + try { + !$input.attr("dir") && $input.attr("dir", "auto"); + } catch (e) {} + return $input; + } + function getBackgroundStyles($el) { + return { + backgroundAttachment: $el.css("background-attachment"), + backgroundClip: $el.css("background-clip"), + backgroundColor: $el.css("background-color"), + backgroundImage: $el.css("background-image"), + backgroundOrigin: $el.css("background-origin"), + backgroundPosition: $el.css("background-position"), + backgroundRepeat: $el.css("background-repeat"), + backgroundSize: $el.css("background-size") + }; + } + function revert($input) { + var www, $wrapper; + www = $input.data(keys.www); + $wrapper = $input.parent().filter(www.selectors.wrapper); + _.each($input.data(keys.attrs), function(val, key) { + _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); + }); + $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input); + if ($wrapper.length) { + $input.detach().insertAfter($wrapper); + $wrapper.remove(); + } + } + function $elOrNull(obj) { + var isValid, $el; + isValid = _.isJQuery(obj) || _.isElement(obj); + $el = isValid ? $(obj).first() : []; + return $el.length ? $el : null; + } + })(); +}); \ No newline at end of file diff --git a/docs/1.2.3/AsyncHTTPClient/search.json b/docs/1.2.3/AsyncHTTPClient/search.json new file mode 100644 index 000000000..f69821621 --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/search.json @@ -0,0 +1 @@ +{"Structs/HTTPClientError.html#/s:s23CustomStringConvertibleP11descriptionSSvp":{"name":"description","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV10invalidURLACvpZ":{"name":"invalidURL","abstract":"

URL provided is invalid.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV9emptyHostACvpZ":{"name":"emptyHost","abstract":"

URL does not contain host.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV17missingSocketPathACvpZ":{"name":"missingSocketPath","abstract":"

URL does not contain a socketPath as a host for http(s)+unix shemes.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV15alreadyShutdownACvpZ":{"name":"alreadyShutdown","abstract":"

Client is shutdown and cannot be used for new requests.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV11emptySchemeACvpZ":{"name":"emptyScheme","abstract":"

URL does not contain scheme.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV17unsupportedSchemeyACSSFZ":{"name":"unsupportedScheme(_:)","abstract":"

Provided URL scheme is not supported, supported schemes are: http and https

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV11readTimeoutACvpZ":{"name":"readTimeout","abstract":"

Request timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV22remoteConnectionClosedACvpZ":{"name":"remoteConnectionClosed","abstract":"

Remote connection was closed unexpectedly.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV9cancelledACvpZ":{"name":"cancelled","abstract":"

Request was cancelled.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV32identityCodingIncorrectlyPresentACvpZ":{"name":"identityCodingIncorrectlyPresent","abstract":"

Request contains invalid identity encoding.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV29chunkedSpecifiedMultipleTimesACvpZ":{"name":"chunkedSpecifiedMultipleTimes","abstract":"

Request contains multiple chunks definitions.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20invalidProxyResponseACvpZ":{"name":"invalidProxyResponse","abstract":"

Proxy response was invalid.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20contentLengthMissingACvpZ":{"name":"contentLengthMissing","abstract":"

Request does not contain Content-Length header.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV27proxyAuthenticationRequiredACvpZ":{"name":"proxyAuthenticationRequired","abstract":"

Proxy Authentication Required.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20redirectLimitReachedACvpZ":{"name":"redirectLimitReached","abstract":"

Redirect Limit reached.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV21redirectCycleDetectedACvpZ":{"name":"redirectCycleDetected","abstract":"

Redirect Cycle detected.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV15uncleanShutdownACvpZ":{"name":"uncleanShutdown","abstract":"

Unclean shutdown.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20traceRequestWithBodyACvpZ":{"name":"traceRequestWithBody","abstract":"

A body was sent in a request with method TRACE.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV23invalidHeaderFieldNamesyACSaySSGFZ":{"name":"invalidHeaderFieldNames(_:)","abstract":"

Header field names contain invalid characters.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV18bodyLengthMismatchACvpZ":{"name":"bodyLengthMismatch","abstract":"

Body length is not equal to Content-Length.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV21writeAfterRequestSentACvpZ":{"name":"writeAfterRequestSent","abstract":"

Body part was written after request was fully sent.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV19incompatibleHeadersACvpZ":{"name":"incompatibleHeaders","abstract":"

Incompatible headers specified, for example Transfer-Encoding and Content-Length.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html":{"name":"HTTPClientError","abstract":"

Possible client errors.

"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP0C0Qa":{"name":"Response","abstract":"

Undocumented

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didSendRequestHead4task_yAA0B0C4TaskCy_0C0QzG_8NIOHTTP1011HTTPRequestH0VtF":{"name":"didSendRequestHead(task:_:)","abstract":"

Called when the request head is sent. Will be called once.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didSendRequestPart4task_yAA0B0C4TaskCy_0C0QzG_3NIO6IODataOtF":{"name":"didSendRequestPart(task:_:)","abstract":"

Called when a part of the request body is sent. Could be called zero or more times.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didSendRequest4taskyAA0B0C4TaskCy_0C0QzG_tF":{"name":"didSendRequest(task:)","abstract":"

Called when the request is fully sent. Will be called once.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_3NIO15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","abstract":"

Called when response head is received. Will be called once.","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_3NIO15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","abstract":"

Called when part of a response body is received. Could be called zero or more times.","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","abstract":"

Called when error was thrown during request execution. Will be called zero or one time only. Request processing will be stopped after that.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","abstract":"

Called when the complete HTTP request is finished. You must return an instance of your Response associated type. Will be called once, except if an error occurred.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html":{"name":"HTTPClientResponseDelegate","abstract":"

HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed."},"Extensions/HTTPClient/NWTLSError.html#/status":{"name":"status","abstract":"

TLS error status. List of TLS errors can be found in

","parent_name":"NWTLSError"},"Extensions/HTTPClient/NWTLSError.html#/init(_:reason:)":{"name":"init(_:reason:)","abstract":"

initialise a NWTLSError

","parent_name":"NWTLSError"},"Extensions/HTTPClient/NWTLSError.html#/description":{"name":"description","parent_name":"NWTLSError"},"Extensions/HTTPClient/NWPOSIXError.html#/errorCode":{"name":"errorCode","abstract":"

POSIX error code (enum)

","parent_name":"NWPOSIXError"},"Extensions/HTTPClient/NWPOSIXError.html#/init(_:reason:)":{"name":"init(_:reason:)","abstract":"

Initialise a NWPOSIXError

","parent_name":"NWPOSIXError"},"Extensions/HTTPClient/NWPOSIXError.html#/description":{"name":"description","parent_name":"NWPOSIXError"},"Extensions/HTTPClient/NWPOSIXError.html":{"name":"NWPOSIXError","parent_name":"HTTPClient"},"Extensions/HTTPClient/NWTLSError.html":{"name":"NWTLSError","parent_name":"HTTPClient"},"Extensions/URL.html#/s:10Foundation3URLV15AsyncHTTPClientE21httpURLWithSocketPath3uriACSgSS_SStcfc":{"name":"init(httpURLWithSocketPath:uri:)","abstract":"

Initializes a newly created HTTP URL connecting to a unix domain socket path. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “http+unix” scheme.

","parent_name":"URL"},"Extensions/URL.html#/s:10Foundation3URLV15AsyncHTTPClientE22httpsURLWithSocketPath3uriACSgSS_SStcfc":{"name":"init(httpsURLWithSocketPath:uri:)","abstract":"

Initializes a newly created HTTPS URL connecting to a unix domain socket path over TLS. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “https+unix” scheme.

","parent_name":"URL"},"Extensions/URL.html":{"name":"URL"},"Extensions/HTTPClient.html":{"name":"HTTPClient"},"Extensions.html#/HTTP1ConnectionProvider":{"name":"HTTP1ConnectionProvider"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B15CopyingDelegateC8Responsea":{"name":"Response","abstract":"

Undocumented

","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B15CopyingDelegateC12chunkHandlerAC3NIO15EventLoopFutureCyytGAE10ByteBufferVc_tcfc":{"name":"init(chunkHandler:)","abstract":"

Undocumented

","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_3NIO15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"HTTPClientCopyingDelegate"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient19ResponseAccumulatorC0C0a":{"name":"Response","abstract":"

Undocumented

","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient19ResponseAccumulatorC7requestAcA0B0C7RequestV_tcfc":{"name":"init(request:)","abstract":"

Undocumented

","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_3NIO15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_3NIO15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"ResponseAccumulator"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC9eventLoop3NIO05EventE0_pvp":{"name":"eventLoop","abstract":"

The EventLoop the delegate will be executed on.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC12futureResult3NIO15EventLoopFutureCyxGvp":{"name":"futureResult","abstract":"

EventLoopFuture for the response returned by this request.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC4waitxyKF":{"name":"wait()","abstract":"

Waits for execution of this request to complete.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC6cancelyyF":{"name":"cancel()","abstract":"

Cancels the request execution.

","parent_name":"Task"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV5basic8username8passwordAESS_SStFZ":{"name":"basic(username:password:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV5basic11credentialsAESS_tFZ":{"name":"basic(credentials:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV6bearer6tokensAESS_tFZ":{"name":"bearer(tokens:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV11headerValueSSvp":{"name":"headerValue","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6method8NIOHTTP110HTTPMethodOvp":{"name":"method","abstract":"

Request HTTP method, defaults to GET.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url10Foundation3URLVvp":{"name":"url","abstract":"

Remote URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6schemeSSvp":{"name":"scheme","abstract":"

Remote HTTP scheme, resolved from URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4hostSSvp":{"name":"host","abstract":"

Remote host, resolved from URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV7headers8NIOHTTP111HTTPHeadersVvp":{"name":"headers","abstract":"

Request custom HTTP Headers, defaults to no headers.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4bodyAC4BodyVSgvp":{"name":"body","abstract":"

Request body, defaults to no body.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4bodyAESS_8NIOHTTP110HTTPMethodOAJ11HTTPHeadersVAC4BodyVSgtKcfc":{"name":"init(url:method:headers:body:)","abstract":"

Create HTTP request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4bodyAE10Foundation3URLV_8NIOHTTP110HTTPMethodOAM11HTTPHeadersVAC4BodyVSgtKcfc":{"name":"init(url:method:headers:body:)","abstract":"

Create an HTTP Request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6useTLSSbvp":{"name":"useTLS","abstract":"

Whether request will be executed using secure socket.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4portSivp":{"name":"port","abstract":"

Resolved port.

","parent_name":"Request"},"Classes/HTTPClient/Body/StreamWriter.html#/s:15AsyncHTTPClient0B0C4BodyV12StreamWriterV7closureAG3NIO15EventLoopFutureCyytGAI6IODataOc_tcfc":{"name":"init(closure:)","abstract":"

Create new StreamWriter

","parent_name":"StreamWriter"},"Classes/HTTPClient/Body/StreamWriter.html#/s:15AsyncHTTPClient0B0C4BodyV12StreamWriterV5writey3NIO15EventLoopFutureCyytGAI6IODataOF":{"name":"write(_:)","abstract":"

Write data to server.

","parent_name":"StreamWriter"},"Classes/HTTPClient/Body/StreamWriter.html":{"name":"StreamWriter","abstract":"

Chunk provider.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6lengthSiSgvp":{"name":"length","abstract":"

Body size. Request validation will be failed with HTTPClientErrors.contentLengthMissing if nil,","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6streamy3NIO15EventLoopFutureCyytGAE12StreamWriterVcvp":{"name":"stream","abstract":"

Body chunk provider.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV10byteBufferyAE3NIO04ByteE0VFZ":{"name":"byteBuffer(_:)","abstract":"

Create and stream body using ByteBuffer.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6stream6length_AESiSg_3NIO15EventLoopFutureCyytGAE12StreamWriterVctFZ":{"name":"stream(length:_:)","abstract":"

Create and stream body using StreamWriter.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV4datayAE10Foundation4DataVFZ":{"name":"data(_:)","abstract":"

Create and stream body using Data.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6stringyAESSFZ":{"name":"string(_:)","abstract":"

Create and stream body using String.

","parent_name":"Body"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4hostSSvp":{"name":"host","abstract":"

Remote host of the request.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV6status8NIOHTTP118HTTPResponseStatusOvp":{"name":"status","abstract":"

Response HTTP status.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7version8NIOHTTP111HTTPVersionVvp":{"name":"version","abstract":"

Response HTTP version.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7headers8NIOHTTP111HTTPHeadersVvp":{"name":"headers","abstract":"

Reponse HTTP headers.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4body3NIO10ByteBufferVSgvp":{"name":"body","abstract":"

Response body.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4host6status7headers4bodyAESS_8NIOHTTP118HTTPResponseStatusOAJ11HTTPHeadersV3NIO10ByteBufferVSgtcfc":{"name":"init(host:status:headers:body:)","abstract":"

Create HTTP Response.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4host6status7version7headers4bodyAESS_8NIOHTTP118HTTPResponseStatusOAK11HTTPVersionVAK11HTTPHeadersV3NIO10ByteBufferVSgtcfc":{"name":"init(host:status:version:headers:body:)","abstract":"

Create HTTP Response.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7cookiesSayAC6CookieVGvp":{"name":"cookies","abstract":"

List of HTTP cookies returned by the server.

","parent_name":"Response"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4nameSSvp":{"name":"name","abstract":"

The name of the cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV5valueSSvp":{"name":"value","abstract":"

The cookie’s string value.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4pathSSvp":{"name":"path","abstract":"

The cookie’s path.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6domainSSSgvp":{"name":"domain","abstract":"

The domain of the cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV7expires10Foundation4DateVSgvp":{"name":"expires","abstract":"

The cookie’s expiration date.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6maxAgeSiSgvp":{"name":"maxAge","abstract":"

The cookie’s age in seconds.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV8httpOnlySbvp":{"name":"httpOnly","abstract":"

Whether the cookie should only be sent to HTTP servers.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6secureSbvp":{"name":"secure","abstract":"

Whether the cookie should only be sent over secure channels.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6header13defaultDomainAESgSS_SStcfc":{"name":"init(header:defaultDomain:)","abstract":"

Create a Cookie by parsing a Set-Cookie header.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4name5value4path6domain7expires6maxAge8httpOnly6secureAESS_S3SSg10Foundation4DateVSgSiSgS2btcfc":{"name":"init(name:value:path:domain:expires:maxAge:httpOnly:secure:)","abstract":"

Create HTTP cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Decompression.html#/s:15AsyncHTTPClient0B0C13DecompressionO8disabledyA2EmF":{"name":"disabled","abstract":"

Decompression is disabled.

","parent_name":"Decompression"},"Classes/HTTPClient/Decompression.html#/s:15AsyncHTTPClient0B0C13DecompressionO7enabledyAE18NIOHTTPCompression20NIOHTTPDecompressionO0C5LimitV_tcAEmF":{"name":"enabled(limit:)","abstract":"

Decompression is enabled.

","parent_name":"Decompression"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV11indifferentAEvpZ":{"name":"indifferent","abstract":"

Event Loop will be selected by the library.

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV8delegate2onAE3NIO0cD0_p_tFZ":{"name":"delegate(on:)","abstract":"

The delegate will be run on the specified EventLoop (and the Channel if possible).

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV18delegateAndChannel2onAE3NIO0cD0_p_tFZ":{"name":"delegateAndChannel(on:)","abstract":"

The delegate and the Channel will be run on the specified EventLoop.

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:s23CustomStringConvertibleP11descriptionSSvp":{"name":"description","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopGroupProvider.html#/s:15AsyncHTTPClient0B0C22EventLoopGroupProviderO6sharedyAE3NIO0cdE0_pcAEmF":{"name":"shared(_:)","abstract":"

EventLoopGroup will be provided by the user. Owner of this group is responsible for its lifecycle.

","parent_name":"EventLoopGroupProvider"},"Classes/HTTPClient/EventLoopGroupProvider.html#/s:15AsyncHTTPClient0B0C22EventLoopGroupProviderO9createNewyA2EmF":{"name":"createNew","abstract":"

EventLoopGroup will be created by the client. When syncShutdown is called, created EventLoopGroup will be shut down as well.

","parent_name":"EventLoopGroupProvider"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV4hostSSvp":{"name":"host","abstract":"

Specifies Proxy server host.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV4portSivp":{"name":"port","abstract":"

Specifies Proxy server port.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV13authorizationAC13AuthorizationVSgvp":{"name":"authorization","abstract":"

Specifies Proxy server authorization.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV6server4host4portAGSS_SitFZ":{"name":"server(host:port:)","abstract":"

Create proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV6server4host4port13authorizationAGSS_SiAC13AuthorizationVSgtFZ":{"name":"server(host:port:authorization:)","abstract":"

Create proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/ConnectionPool.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14ConnectionPoolV11idleTimeout3NIO10TimeAmountVvp":{"name":"idleTimeout","abstract":"

Undocumented

","parent_name":"ConnectionPool"},"Classes/HTTPClient/Configuration/ConnectionPool.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14ConnectionPoolV11idleTimeoutAG3NIO10TimeAmountV_tcfc":{"name":"init(idleTimeout:)","abstract":"

Undocumented

","parent_name":"ConnectionPool"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08RedirectC0V8disallowAGvpZ":{"name":"disallow","abstract":"

Redirects are not followed.

","parent_name":"RedirectConfiguration"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08RedirectC0V6follow3max11allowCyclesAGSi_SbtFZ":{"name":"follow(max:allowCycles:)","abstract":"

Redirects are followed with a specified limit.

","parent_name":"RedirectConfiguration"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV7connect3NIO10TimeAmountVSgvp":{"name":"connect","abstract":"

Specifies connect timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV4read3NIO10TimeAmountVSgvp":{"name":"read","abstract":"

Specifies read timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV7connect4readAG3NIO10TimeAmountVSg_AMtcfc":{"name":"init(connect:read:)","abstract":"

Create timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC06NIOSSL16TLSConfigurationVSgvp":{"name":"tlsConfiguration","abstract":"

TLS configuration, defaults to TLSConfiguration.forClient().

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08redirectC0AE08RedirectC0Vvp":{"name":"redirectConfiguration","abstract":"

Enables following 3xx redirects automatically, defaults to RedirectConfiguration().

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7timeoutAE7TimeoutVvp":{"name":"timeout","abstract":"

Default client timeout, defaults to no read timeout and 10 seconds connect timeout.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14connectionPoolAE010ConnectionE0Vvp":{"name":"connectionPool","abstract":"

Connection pool configuration.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5proxyAE5ProxyVSgvp":{"name":"proxy","abstract":"

Upstream proxy, defaults to no proxy.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV13decompressionAC13DecompressionOvp":{"name":"decompression","abstract":"

Enables automatic body decompression. Supported algorithms are gzip and deflate.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV24ignoreUncleanSSLShutdownSbvp":{"name":"ignoreUncleanSSLShutdown","abstract":"

Ignore TLS unclean shutdown error, defaults to false.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC008redirectC07timeout14connectionPool5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL16TLSConfigurationVSg_AE08RedirectC0VSgAE7TimeoutVAE010ConnectionH0VAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(tlsConfiguration:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC008redirectC07timeout5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL16TLSConfigurationVSg_AE08RedirectC0VSgAE7TimeoutVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(tlsConfiguration:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout38maximumAllowedIdleTimeInConnectionPool5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutV3NIO0K6AmountVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:maximumAllowedIdleTimeInConnectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout14connectionPool5proxy24ignoreUncleanSSLShutdown13decompression24backgroundActivityLoggerAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutV3NIO10TimeAmountVAE5ProxyVSgSbAC13DecompressionO7Logging0Q0VSgtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:backgroundActivityLogger:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/Timeout.html":{"name":"Timeout","abstract":"

Timeout configuration.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html":{"name":"RedirectConfiguration","abstract":"

Specifies redirect processing settings.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/ConnectionPool.html":{"name":"ConnectionPool","abstract":"

Connection pool configuration.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/Proxy.html":{"name":"Proxy","abstract":"

Proxy server configuration","parent_name":"Configuration"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C14eventLoopGroup3NIO05EventdE0_pvp":{"name":"eventLoopGroup","abstract":"

Undocumented

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C22eventLoopGroupProvider13configurationA2C05EventdeF0O_AC13ConfigurationVtcfc":{"name":"init(eventLoopGroupProvider:configuration:)","abstract":"

Create an HTTPClient with specified EventLoopGroup provider and configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C22eventLoopGroupProvider13configuration24backgroundActivityLoggerA2C05EventdeF0O_AC13ConfigurationV7Logging0J0Vtcfc":{"name":"init(eventLoopGroupProvider:configuration:backgroundActivityLogger:)","abstract":"

Create an HTTPClient with specified EventLoopGroup provider and configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C12syncShutdownyyKF":{"name":"syncShutdown()","abstract":"

Shuts down the client and EventLoopGroup if it was created by the client.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C8shutdown5queue_y8Dispatch0E5QueueC_ys5Error_pSgctF":{"name":"shutdown(queue:_:)","abstract":"

Shuts down the client and event loop gracefully. This function is clearly an outlier in that it uses a completion","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3get3url8deadline3NIO15EventLoopFutureCyAC8ResponseVGSS_AG11NIODeadlineVSgtF":{"name":"get(url:deadline:)","abstract":"

Execute GET request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3get3url8deadline6logger3NIO15EventLoopFutureCyAC8ResponseVGSS_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"get(url:deadline:logger:)","abstract":"

Execute GET request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C4post3url4body8deadline3NIO15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"post(url:body:deadline:)","abstract":"

Execute POST request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C4post3url4body8deadline6logger3NIO15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"post(url:body:deadline:logger:)","abstract":"

Execute POST request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C5patch3url4body8deadline3NIO15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"patch(url:body:deadline:)","abstract":"

Execute PATCH request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C5patch3url4body8deadline6logger3NIO15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"patch(url:body:deadline:logger:)","abstract":"

Execute PATCH request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3put3url4body8deadline3NIO15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"put(url:body:deadline:)","abstract":"

Execute PUT request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3put3url4body8deadline6logger3NIO15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"put(url:body:deadline:logger:)","abstract":"

Execute PUT request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C6delete3url8deadline3NIO15EventLoopFutureCyAC8ResponseVGSS_AG11NIODeadlineVSgtF":{"name":"delete(url:deadline:)","abstract":"

Execute DELETE request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C6delete3url8deadline6logger3NIO15EventLoopFutureCyAC8ResponseVGSS_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"delete(url:deadline:logger:)","abstract":"

Execute DELETE request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_3url4body8deadline6logger3NIO15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_SSAC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:url:body:deadline:logger:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_10socketPath03urlE04body8deadline6logger3NIO15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_S2SAC4BodyVSgAJ11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:socketPath:urlPath:body:deadline:logger:)","abstract":"

Execute arbitrary HTTP+UNIX request to a unix domain socket path, using the specified URL as the request to send to the server.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_16secureSocketPath03urlF04body8deadline6logger3NIO15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_S2SAC4BodyVSgAJ11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:secureSocketPath:urlPath:body:deadline:logger:)","abstract":"

Execute arbitrary HTTPS+UNIX request to a unix domain socket path over TLS, using the specified URL as the request to send to the server.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8deadline3NIO15EventLoopFutureCyAC8ResponseVGAC7RequestV_AG11NIODeadlineVSgtF":{"name":"execute(request:deadline:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8deadline6logger3NIO15EventLoopFutureCyAC8ResponseVGAC7RequestV_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"execute(request:deadline:logger:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request9eventLoop8deadline3NIO05EventF6FutureCyAC8ResponseVGAC7RequestV_AC0iF10PreferenceVAH11NIODeadlineVSgtF":{"name":"execute(request:eventLoop:deadline:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request9eventLoop8deadline6logger3NIO05EventF6FutureCyAC8ResponseVGAC7RequestV_AC0jF10PreferenceVAI11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(request:eventLoop:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate8deadlineAC4TaskCy_8ResponseQzGAC7RequestV_x3NIO11NIODeadlineVSgtAA0bH8DelegateRzlF":{"name":"execute(request:delegate:deadline:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate8deadline6loggerAC4TaskCy_8ResponseQzGAC7RequestV_x3NIO11NIODeadlineVSg7Logging6LoggerVtAA0bI8DelegateRzlF":{"name":"execute(request:delegate:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate9eventLoop8deadlineAC4TaskCy_8ResponseQzGAC7RequestV_xAC05EventG10PreferenceV3NIO11NIODeadlineVSgtAA0bJ8DelegateRzlF":{"name":"execute(request:delegate:eventLoop:deadline:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate9eventLoop8deadline6loggerAC4TaskCy_8ResponseQzGAC7RequestV_xAC05EventG10PreferenceV3NIO11NIODeadlineVSg7Logging6LoggerVSgtAA0bK8DelegateRzlF":{"name":"execute(request:delegate:eventLoop:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Configuration.html":{"name":"Configuration","abstract":"

HTTPClient configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient/EventLoopGroupProvider.html":{"name":"EventLoopGroupProvider","abstract":"

Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

","parent_name":"HTTPClient"},"Classes/HTTPClient/EventLoopPreference.html":{"name":"EventLoopPreference","abstract":"

Specifies how the library will treat event loop passed by the user.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Decompression.html":{"name":"Decompression","abstract":"

Specifies decompression settings.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Cookie.html":{"name":"Cookie","abstract":"

A representation of an HTTP cookie.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Response.html":{"name":"Response","abstract":"

Represent HTTP response.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Body.html":{"name":"Body","abstract":"

Represent request body.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Request.html":{"name":"Request","abstract":"

Represent HTTP request.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Authorization.html":{"name":"Authorization","abstract":"

HTTP authentication

","parent_name":"HTTPClient"},"Classes/HTTPClient/Task.html":{"name":"Task","abstract":"

Response execution context. Will be created by the library and could be used for obtaining","parent_name":"HTTPClient"},"Classes/FileDownloadDelegate/Progress.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8ProgressV10totalBytesSiSgvp":{"name":"totalBytes","abstract":"

Undocumented

","parent_name":"Progress"},"Classes/FileDownloadDelegate/Progress.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8ProgressV13receivedBytesSivp":{"name":"receivedBytes","abstract":"

Undocumented

","parent_name":"Progress"},"Classes/FileDownloadDelegate/Progress.html":{"name":"Progress","abstract":"

The response type for this delegate: the total count of bytes as reported by the response","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8Responsea":{"name":"Response","abstract":"

Undocumented

","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient20FileDownloadDelegateC4path4pool10reportHead0H8ProgressACSS_3NIO13NIOThreadPoolCy8NIOHTTP1012HTTPResponseI0VcSgyAC0J0VcSgtKcfc":{"name":"init(path:pool:reportHead:reportProgress:)","abstract":"

Initializes a new file download delegate.

","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_3NIO15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_3NIO15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html":{"name":"FileDownloadDelegate","abstract":"

Handles a streaming download to a given file path, allowing headers and progress to be reported.

"},"Classes/HTTPClient.html":{"name":"HTTPClient","abstract":"

HTTPClient class provides API for request execution.

"},"Classes/ResponseAccumulator.html":{"name":"ResponseAccumulator","abstract":"

Undocumented

"},"Classes/HTTPClientCopyingDelegate.html":{"name":"HTTPClientCopyingDelegate","abstract":"

Undocumented

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Extensions.html":{"name":"Extensions","abstract":"

The following extensions are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"},"Structs.html":{"name":"Structures","abstract":"

The following structures are available globally.

"}} \ No newline at end of file diff --git a/docs/1.2.3/AsyncHTTPClient/undocumented.json b/docs/1.2.3/AsyncHTTPClient/undocumented.json new file mode 100644 index 000000000..47af1d76f --- /dev/null +++ b/docs/1.2.3/AsyncHTTPClient/undocumented.json @@ -0,0 +1,159 @@ +{ + "warnings": [ + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 23, + "symbol": "FileDownloadDelegate.Progress.totalBytes", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 24, + "symbol": "FileDownloadDelegate.Progress.receivedBytes", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 29, + "symbol": "FileDownloadDelegate.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 66, + "symbol": "HTTPClient.eventLoopGroup", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 653, + "symbol": "HTTPClient.Configuration.init(tlsConfiguration:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 669, + "symbol": "HTTPClient.Configuration.init(tlsConfiguration:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 686, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:maximumAllowedIdleTimeInConnectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 702, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:backgroundActivityLogger:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 719, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 867, + "symbol": "HTTPClient.Configuration.ConnectionPool.idleTimeout", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 869, + "symbol": "HTTPClient.Configuration.ConnectionPool.init(idleTimeout:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 319, + "symbol": "HTTPClient.Authorization.basic(username:password:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 323, + "symbol": "HTTPClient.Authorization.basic(credentials:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 327, + "symbol": "HTTPClient.Authorization.bearer(tokens:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 331, + "symbol": "HTTPClient.Authorization.headerValue", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 342, + "symbol": "ResponseAccumulator", + "symbol_kind": "source.lang.swift.decl.class", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 343, + "symbol": "ResponseAccumulator.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 356, + "symbol": "ResponseAccumulator.init(request:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 425, + "symbol": "HTTPClientResponseDelegate.Response", + "symbol_kind": "source.lang.swift.decl.associatedtype", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 37, + "symbol": "HTTPClientCopyingDelegate", + "symbol_kind": "source.lang.swift.decl.class", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 38, + "symbol": "HTTPClientCopyingDelegate.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 42, + "symbol": "HTTPClientCopyingDelegate.init(chunkHandler:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + } + ], + "source_directory": "/code" +} \ No newline at end of file diff --git a/docs/1.4.1/AsyncHTTPClient/Classes.html b/docs/1.4.1/AsyncHTTPClient/Classes.html new file mode 100644 index 000000000..b585a0226 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes.html @@ -0,0 +1,314 @@ + + + + Classes Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Classes

+

The following classes are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + FileDownloadDelegate + +
    +
    +
    +
    +
    +
    +

    Handles a streaming download to a given file path, allowing headers and progress to be reported.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class FileDownloadDelegate : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + HTTPClient + +
    +
    +
    +
    +
    +
    +

    HTTPClient class provides API for request execution.

    + +

    Example:

    +
        let client = HTTPClient(eventLoopGroupProvider: .createNew)
    +    client.get(url: "https://swift.org/", deadline: .now() + .seconds(1)).whenComplete { result in
    +        switch result {
    +        case .failure(let error):
    +            // process error
    +        case .success(let response):
    +            if let response.status == .ok {
    +                // handle response
    +            } else {
    +                // handle remote error
    +            }
    +        }
    +    }
    +
    + +

    It is important to close the client instance, for example in a defer statement, after use to cleanly shutdown the underlying NIO EventLoopGroup:

    +
        try client.syncShutdown()
    +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class HTTPClient
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + ResponseAccumulator + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class ResponseAccumulator : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Undocumented

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class HTTPClientCopyingDelegate : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Classes/FileDownloadDelegate.html b/docs/1.4.1/AsyncHTTPClient/Classes/FileDownloadDelegate.html new file mode 100644 index 000000000..4f6a0c533 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes/FileDownloadDelegate.html @@ -0,0 +1,457 @@ + + + + FileDownloadDelegate Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

FileDownloadDelegate

+
+
+ +
public final class FileDownloadDelegate : HTTPClientResponseDelegate
+ +
+
+

Handles a streaming download to a given file path, allowing headers and progress to be reported.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + Progress + +
    +
    +
    +
    +
    +
    +

    The response type for this delegate: the total count of bytes as reported by the response +“Content-Length” header (if available) and the count of bytes downloaded.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Progress
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public typealias Response = Progress
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Initializes a new file download delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(
    +    path: String,
    +    pool: NIOThreadPool = NIOThreadPool(numberOfThreads: 1),
    +    reportHead: ((HTTPResponseHead) -> Void)? = nil,
    +    reportProgress: ((Progress) -> Void)? = nil
    +) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + path + + +
    +

    Path to a file you’d like to write the download to.

    +
    +
    + + pool + + +
    +

    A thread pool to use for asynchronous file I/O.

    +
    +
    + + reportHead + + +
    +

    A closure called when the response head is available.

    +
    +
    + + reportProgress + + +
    +

    A closure called when a body chunk has been downloaded, with +the total byte count and download byte count passed to it as arguments. The callbacks +will be invoked in the same threading context that the delegate itself is invoked, +as controlled by EventLoopPreference.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveHead(
    +    task: HTTPClient.Task<Response>,
    +    _ head: HTTPResponseHead
    +) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveBodyPart(
    +    task: HTTPClient.Task<Response>,
    +    _ buffer: ByteBuffer
    +) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveError(task: HTTPClient.Task<Progress>, _ error: Error)
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didFinishRequest(task: HTTPClient.Task<Response>) throws -> Response
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html b/docs/1.4.1/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html new file mode 100644 index 000000000..4e230ab98 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html @@ -0,0 +1,241 @@ + + + + Progress Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Progress

+
+
+ +
public struct Progress
+ +
+
+

The response type for this delegate: the total count of bytes as reported by the response +“Content-Length” header (if available) and the count of bytes downloaded.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + totalBytes + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var totalBytes: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + receivedBytes + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var receivedBytes: Int
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient.html b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient.html new file mode 100644 index 000000000..536905524 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient.html @@ -0,0 +1,2472 @@ + + + + HTTPClient Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClient

+
+
+ +
public class HTTPClient
+ +
+
+

HTTPClient class provides API for request execution.

+ +

Example:

+
    let client = HTTPClient(eventLoopGroupProvider: .createNew)
+    client.get(url: "https://swift.org/", deadline: .now() + .seconds(1)).whenComplete { result in
+        switch result {
+        case .failure(let error):
+            // process error
+        case .success(let response):
+            if let response.status == .ok {
+                // handle response
+            } else {
+                // handle remote error
+            }
+        }
+    }
+
+ +

It is important to close the client instance, for example in a defer statement, after use to cleanly shutdown the underlying NIO EventLoopGroup:

+
    try client.syncShutdown()
+
+ + +
+
+ +
+
+
+
    +
  • +
    + + + + eventLoopGroup + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let eventLoopGroup: EventLoopGroup
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTPClient with specified EventLoopGroup provider and configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public convenience init(eventLoopGroupProvider: EventLoopGroupProvider,
    +                        configuration: Configuration = Configuration())
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + eventLoopGroupProvider + + +
    +

    Specify how EventLoopGroup will be created.

    +
    +
    + + configuration + + +
    +

    Client configuration.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTPClient with specified EventLoopGroup provider and configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public required init(eventLoopGroupProvider: EventLoopGroupProvider,
    +                     configuration: Configuration = Configuration(),
    +                     backgroundActivityLogger: Logger)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + eventLoopGroupProvider + + +
    +

    Specify how EventLoopGroup will be created.

    +
    +
    + + configuration + + +
    +

    Client configuration.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + syncShutdown() + +
    +
    +
    +
    +
    +
    +

    Shuts down the client and EventLoopGroup if it was created by the client.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func syncShutdown() throws
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + shutdown(queue:_:) + +
    +
    +
    +
    +
    +
    +

    Shuts down the client and event loop gracefully. This function is clearly an outlier in that it uses a completion +callback instead of an EventLoopFuture. The reason for that is that NIO’s EventLoopFutures will call back on an event loop. +The virtue of this function is to shut the event loop down. To work around that we call back on a DispatchQueue +instead.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func shutdown(queue: DispatchQueue = .global(), _ callback: @escaping (Error?) -> Void)
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + get(url:deadline:) + +
    +
    +
    +
    +
    +
    +

    Execute GET request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func get(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute GET request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func get(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute POST request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute POST request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PATCH request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PATCH request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PUT request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PUT request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + delete(url:deadline:) + +
    +
    +
    +
    +
    +
    +

    Execute DELETE request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func delete(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    The time when the request must have been completed by.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute DELETE request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func delete(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    The time when the request must have been completed by.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + url + + +
    +

    Request url.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP+UNIX request to a unix domain socket path, using the specified URL as the request to send to the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, socketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + urlPath + + +
    +

    The URL path and query that will be sent to the server.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTPS+UNIX request to a unix domain socket path over TLS, using the specified URL as the request to send to the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, secureSocketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + secureSocketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + urlPath + + +
    +

    The URL path and query that will be sent to the server.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, eventLoop: EventLoopPreference, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request,
    +                    eventLoop eventLoopPreference: EventLoopPreference,
    +                    deadline: NIODeadline? = nil,
    +                    logger: Logger?) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          deadline: NIODeadline? = nil) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          deadline: NIODeadline? = nil,
    +                                                          logger: Logger) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          eventLoop eventLoopPreference: EventLoopPreference,
    +                                                          deadline: NIODeadline? = nil) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          eventLoop eventLoopPreference: EventLoopPreference,
    +                                                          deadline: NIODeadline? = nil,
    +                                                          logger originalLogger: Logger?) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + Configuration + +
    +
    +
    +
    +
    +
    +

    HTTPClient configuration.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Configuration
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum EventLoopGroupProvider
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + EventLoopPreference + +
    +
    +
    +
    +
    +
    +

    Specifies how the library will treat event loop passed by the user.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct EventLoopPreference
    +
    extension HTTPClient.EventLoopPreference: CustomStringConvertible
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Decompression + +
    +
    +
    +
    +
    +
    +

    Specifies decompression settings.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum Decompression
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Cookie + +
    +
    +
    +
    +
    +
    +

    A representation of an HTTP cookie.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Cookie
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Represent HTTP response.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Response
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Body + +
    +
    +
    +
    +
    +
    +

    Represent request body.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Body
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Request + +
    +
    +
    +
    +
    +
    +

    Represent HTTP request.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Request
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Authorization + +
    +
    +
    +
    +
    +
    +

    HTTP authentication

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Authorization : Hashable
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Task + +
    +
    +
    +
    +
    +
    +

    Response execution context. Will be created by the library and could be used for obtaining +EventLoopFuture<Response> of the execution or cancellation of the execution.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class Task<Response>
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + NWPOSIXError + +
    +
    +
    +
    +
    +
    + + See more +
    +
    +
    +
  • +
  • +
    + + + + NWTLSError + +
    +
    +
    +
    +
    +
    + + See more +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Authorization.html b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Authorization.html new file mode 100644 index 000000000..f7e63b323 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Authorization.html @@ -0,0 +1,300 @@ + + + + Authorization Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Authorization

+
+
+ +
public struct Authorization : Hashable
+ +
+
+

HTTP authentication

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Body.html b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Body.html new file mode 100644 index 000000000..5e32b098c --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Body.html @@ -0,0 +1,481 @@ + + + + Body Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Body

+
+
+ +
public struct Body
+ +
+
+

Represent request body.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + StreamWriter + +
    +
    +
    +
    +
    +
    +

    Chunk provider.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct StreamWriter
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + length + +
    +
    +
    +
    +
    +
    +

    Body size. Request validation will be failed with HTTPClientErrors.contentLengthMissing if nil, +unless Trasfer-Encoding: chunked header is set.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var length: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + stream + +
    +
    +
    +
    +
    +
    +

    Body chunk provider.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var stream: (StreamWriter) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + byteBuffer(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using ByteBuffer.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func byteBuffer(_ buffer: ByteBuffer) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + buffer + + +
    +

    Body ByteBuffer representation.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + stream(length:_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using StreamWriter.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func stream(length: Int? = nil, _ stream: @escaping (StreamWriter) -> EventLoopFuture<Void>) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + length + + +
    +

    Body size. Request validation will be failed with HTTPClientErrors.contentLengthMissing if nil, + unless Transfer-Encoding: chunked header is set.

    +
    +
    + + stream + + +
    +

    Body chunk provider.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + data(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using Data.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func data(_ data: Data) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    Body Data representation.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + string(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using String.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func string(_ string: String) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + string + + +
    +

    Body String representation.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html new file mode 100644 index 000000000..13f114ecd --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html @@ -0,0 +1,278 @@ + + + + StreamWriter Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

StreamWriter

+
+
+ +
public struct StreamWriter
+ +
+
+

Chunk provider.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + init(closure:) + +
    +
    +
    +
    +
    +
    +

    Create new StreamWriter

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(closure: @escaping (IOData) -> EventLoopFuture<Void>)
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + closure + + +
    +

    function that will be called to write actual bytes to the channel.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + write(_:) + +
    +
    +
    +
    +
    +
    +

    Write data to server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func write(_ data: IOData) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    IOData to write.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Configuration.html b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Configuration.html new file mode 100644 index 000000000..ae8745fcd --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Configuration.html @@ -0,0 +1,714 @@ + + + + Configuration Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Configuration

+
+
+ +
public struct Configuration
+ +
+
+

HTTPClient configuration.

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html new file mode 100644 index 000000000..2248ca438 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html @@ -0,0 +1,240 @@ + + + + ConnectionPool Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

ConnectionPool

+
+
+ +
public struct ConnectionPool : Hashable
+ +
+
+

Connection pool configuration.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + idleTimeout + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var idleTimeout: TimeAmount
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + init(idleTimeout:) + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(idleTimeout: TimeAmount = .seconds(60))
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html new file mode 100644 index 000000000..03114db60 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html @@ -0,0 +1,478 @@ + + + + Proxy Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Proxy

+
+
+ +
public struct Proxy
+ +
+
+

Proxy server configuration +Specifies the remote address of an HTTP proxy.

+ +

Adding an Proxy to your client’s HTTPClient.Configuration +will cause requests to be passed through the specified proxy using the +HTTP CONNECT method.

+ +

If a TLSConfiguration is used in conjunction with HTTPClient.Configuration.Proxy, +TLS will be established after successful proxy, between your client +and the destination server.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server host.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + port + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server port.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var port: Int
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + authorization + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server authorization.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var authorization: HTTPClient.Authorization? { get set }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + server(host:port:) + +
    +
    +
    +
    +
    +
    +

    Create a HTTP proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func server(host: String, port: Int) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + host + + +
    +

    proxy server host.

    +
    +
    + + port + + +
    +

    proxy server port.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a HTTP proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func server(host: String, port: Int, authorization: HTTPClient.Authorization? = nil) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + host + + +
    +

    proxy server host.

    +
    +
    + + port + + +
    +

    proxy server port.

    +
    +
    + + authorization + + +
    +

    proxy server authorization.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a SOCKSv5 proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func socksServer(host: String, port: Int = 1080) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + host + + +
    +

    The SOCKSv5 proxy address.

    +
    +
    + + port + + +
    +

    The SOCKSv5 proxy port, defaults to 1080.

    +
    +
    +
    +
    +

    Return Value

    +

    A new instance of Proxy configured to connect to a SOCKSv5 server.

    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html new file mode 100644 index 000000000..4f3fb2f1f --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html @@ -0,0 +1,276 @@ + + + + RedirectConfiguration Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

RedirectConfiguration

+
+
+ +
public struct RedirectConfiguration
+ +
+
+

Specifies redirect processing settings.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + disallow + +
    +
    +
    +
    +
    +
    +

    Redirects are not followed.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let disallow: HTTPClient.Configuration.RedirectConfiguration
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Redirects are followed with a specified limit.

    +
    +

    Warning

    +

    Cycle detection will keep all visited URLs in memory which means a malicious server could use this as a denial-of-service vector.

    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func follow(max: Int, allowCycles: Bool) -> RedirectConfiguration
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + max + + +
    +

    The maximum number of allowed redirects.

    +
    +
    + + allowCycles + + +
    +

    Whether cycles are allowed.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html new file mode 100644 index 000000000..660338ee3 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html @@ -0,0 +1,301 @@ + + + + Timeout Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Timeout

+
+
+ +
public struct Timeout
+ +
+
+

Timeout configuration.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + connect + +
    +
    +
    +
    +
    +
    +

    Specifies connect timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var connect: TimeAmount?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + read + +
    +
    +
    +
    +
    +
    +

    Specifies read timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var read: TimeAmount?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + init(connect:read:) + +
    +
    +
    +
    +
    +
    +

    Create timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(connect: TimeAmount? = nil, read: TimeAmount? = nil)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + connect + + +
    +

    connect timeout.

    +
    +
    + + read + + +
    +

    read timeout.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Cookie.html b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Cookie.html new file mode 100644 index 000000000..04c170de9 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Cookie.html @@ -0,0 +1,618 @@ + + + + Cookie Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Cookie

+
+
+ +
public struct Cookie
+ +
+
+

A representation of an HTTP cookie.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + name + +
    +
    +
    +
    +
    +
    +

    The name of the cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var name: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + value + +
    +
    +
    +
    +
    +
    +

    The cookie’s string value.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var value: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + path + +
    +
    +
    +
    +
    +
    +

    The cookie’s path.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var path: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + domain + +
    +
    +
    +
    +
    +
    +

    The domain of the cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var domain: String?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + expires + +
    +
    +
    +
    +
    +
    +

    The cookie’s expiration date.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var expires: Date?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + maxAge + +
    +
    +
    +
    +
    +
    +

    The cookie’s age in seconds.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var maxAge: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + httpOnly + +
    +
    +
    +
    +
    +
    +

    Whether the cookie should only be sent to HTTP servers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var httpOnly: Bool
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + secure + +
    +
    +
    +
    +
    +
    +

    Whether the cookie should only be sent over secure channels.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var secure: Bool
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a Cookie by parsing a Set-Cookie header.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(header: String, defaultDomain: String)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + header + + +
    +

    String representation of the Set-Cookie response header.

    +
    +
    + + defaultDomain + + +
    +

    Default domain to use if cookie was sent without one.

    +
    +
    +
    +
    +

    Return Value

    +

    nil if the header is invalid.

    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(name: String, value: String, path: String = "/", domain: String? = nil, expires: Date? = nil, maxAge: Int? = nil, httpOnly: Bool = false, secure: Bool = false)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + name + + +
    +

    The name of the cookie.

    +
    +
    + + value + + +
    +

    The cookie’s string value.

    +
    +
    + + path + + +
    +

    The cookie’s path.

    +
    +
    + + domain + + +
    +

    The domain of the cookie, defaults to nil.

    +
    +
    + + expires + + +
    +

    The cookie’s expiration date, defaults to nil.

    +
    +
    + + maxAge + + +
    +

    The cookie’s age in seconds, defaults to nil.

    +
    +
    + + httpOnly + + +
    +

    Whether this cookie should be used by HTTP servers only, defaults to false.

    +
    +
    + + secure + + +
    +

    Whether this cookie should only be sent using secure channels, defaults to false.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Decompression.html b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Decompression.html new file mode 100644 index 000000000..c632917f8 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Decompression.html @@ -0,0 +1,240 @@ + + + + Decompression Enumeration Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Decompression

+
+
+ +
public enum Decompression
+ +
+
+

Specifies decompression settings.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + disabled + +
    +
    +
    +
    +
    +
    +

    Decompression is disabled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case disabled
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + enabled(limit:) + +
    +
    +
    +
    +
    +
    +

    Decompression is enabled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case enabled(limit: NIOHTTPDecompression.DecompressionLimit)
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html new file mode 100644 index 000000000..5c4056ec5 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html @@ -0,0 +1,240 @@ + + + + EventLoopGroupProvider Enumeration Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

EventLoopGroupProvider

+
+
+ +
public enum EventLoopGroupProvider
+ +
+
+

Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + shared(_:) + +
    +
    +
    +
    +
    +
    +

    EventLoopGroup will be provided by the user. Owner of this group is responsible for its lifecycle.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case shared(EventLoopGroup)
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + createNew + +
    +
    +
    +
    +
    +
    +

    EventLoopGroup will be created by the client. When syncShutdown is called, created EventLoopGroup will be shut down as well.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case createNew
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html new file mode 100644 index 000000000..fe290b953 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html @@ -0,0 +1,307 @@ + + + + EventLoopPreference Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

EventLoopPreference

+
+
+ +
public struct EventLoopPreference
+
extension HTTPClient.EventLoopPreference: CustomStringConvertible
+ +
+
+

Specifies how the library will treat event loop passed by the user.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + indifferent + +
    +
    +
    +
    +
    +
    +

    Event Loop will be selected by the library.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let indifferent: HTTPClient.EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + delegate(on:) + +
    +
    +
    +
    +
    +
    +

    The delegate will be run on the specified EventLoop (and the Channel if possible).

    + +

    This will call the configured delegate on eventLoop and will try to use a Channel on the same +EventLoop but will not establish a new network connection just to satisfy the EventLoop preference if +another existing connection on a different EventLoop is readily available from a connection pool.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func delegate(on eventLoop: EventLoop) -> EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The delegate and the Channel will be run on the specified EventLoop.

    + +

    Use this for use-cases where you prefer a new connection to be established over re-using an existing +connection that might be on a different EventLoop.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func delegateAndChannel(on eventLoop: EventLoop) -> EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var description: String { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/NWPOSIXError.html b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/NWPOSIXError.html new file mode 100644 index 000000000..6a094a48d --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/NWPOSIXError.html @@ -0,0 +1,225 @@ + + + + NWPOSIXError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

NWPOSIXError

+ +
+
+ +
+
+
+
    +
  • +
    + + + + errorCode + +
    +
    +
    +
    +
    +
    +

    POSIX error code (enum)

    + +
    +
    +
    +
  • +
  • +
    + + + + init(_:reason:) + +
    +
    +
    +
    +
    +
    +

    Initialise a NWPOSIXError

    + +
    +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/NWTLSError.html b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/NWTLSError.html new file mode 100644 index 000000000..2d1660d9d --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/NWTLSError.html @@ -0,0 +1,225 @@ + + + + NWTLSError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

NWTLSError

+ +
+
+ +
+
+
+
    +
  • +
    + + + + status + +
    +
    +
    +
    +
    +
    +

    TLS error status. List of TLS errors can be found in

    + +
    +
    +
    +
  • +
  • +
    + + + + init(_:reason:) + +
    +
    +
    +
    +
    +
    +

    initialise a NWTLSError

    + +
    +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Request.html b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Request.html new file mode 100644 index 000000000..e610c4526 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Request.html @@ -0,0 +1,878 @@ + + + + Request Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Request

+
+
+ +
public struct Request
+ +
+
+

Represent HTTP request.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + method + +
    +
    +
    +
    +
    +
    +

    Request HTTP method, defaults to GET.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let method: HTTPMethod
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + url + +
    +
    +
    +
    +
    +
    +

    Remote URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let url: URL
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + scheme + +
    +
    +
    +
    +
    +
    +

    Remote HTTP scheme, resolved from URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let scheme: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Remote host, resolved from URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + headers + +
    +
    +
    +
    +
    +
    +

    Request custom HTTP Headers, defaults to no headers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var headers: HTTPHeaders
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + body + +
    +
    +
    +
    +
    +
    +

    Request body, defaults to no body.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var body: Body?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + tlsConfiguration + +
    +
    +
    +
    +
    +
    +

    Request-specific TLS configuration, defaults to no request-specific TLS configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var tlsConfiguration: TLSConfiguration?
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP request.

    +
    +

    Throws

    +
      +
    • invalidURL if URL cannot be parsed.
    • +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: String, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + version + + +
    +

    HTTP version.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP request.

    +
    +

    Throws

    +
      +
    • invalidURL if URL cannot be parsed.
    • +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: String, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil, tlsConfiguration: TLSConfiguration?) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + version + + +
    +

    HTTP version.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + tlsConfiguration + + +
    +

    Request TLS configuration

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTP Request.

    +
    +

    Throws

    +
      +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    • missingSocketPath if URL does not contains a socketPath as an encoded host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTP Request.

    +
    +

    Throws

    +
      +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    • missingSocketPath if URL does not contains a socketPath as an encoded host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil, tlsConfiguration: TLSConfiguration?) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + tlsConfiguration + + +
    +

    Request TLS configuration

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + useTLS + +
    +
    +
    +
    +
    +
    +

    Whether request will be executed using secure socket.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var useTLS: Bool { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + port + +
    +
    +
    +
    +
    +
    +

    Resolved port.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var port: Int { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Response.html b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Response.html new file mode 100644 index 000000000..c31e20e93 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Response.html @@ -0,0 +1,543 @@ + + + + Response Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Response

+
+
+ +
public struct Response
+ +
+
+

Represent HTTP response.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Remote host of the request.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + status + +
    +
    +
    +
    +
    +
    +

    Response HTTP status.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var status: HTTPResponseStatus
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + version + +
    +
    +
    +
    +
    +
    +

    Response HTTP version.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var version: HTTPVersion
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + headers + +
    +
    +
    +
    +
    +
    +

    Reponse HTTP headers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var headers: HTTPHeaders
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + body + +
    +
    +
    +
    +
    +
    +

    Response body.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var body: ByteBuffer?
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP Response.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    @available(*, deprecated, renamed: "init(host:status:version:headers:body:﹚")
    +public init(host: String, status: HTTPResponseStatus, headers: HTTPHeaders, body: ByteBuffer?)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + host + + +
    +

    Remote host of the request.

    +
    +
    + + status + + +
    +

    Response HTTP status.

    +
    +
    + + headers + + +
    +

    Reponse HTTP headers.

    +
    +
    + + body + + +
    +

    Response body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP Response.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(host: String, status: HTTPResponseStatus, version: HTTPVersion, headers: HTTPHeaders, body: ByteBuffer?)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + host + + +
    +

    Remote host of the request.

    +
    +
    + + status + + +
    +

    Response HTTP status.

    +
    +
    + + version + + +
    +

    Response HTTP version.

    +
    +
    + + headers + + +
    +

    Reponse HTTP headers.

    +
    +
    + + body + + +
    +

    Response body.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + cookies + +
    +
    +
    +
    +
    +
    +

    List of HTTP cookies returned by the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var cookies: [HTTPClient.Cookie] { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Task.html b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Task.html new file mode 100644 index 000000000..47aece8ee --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClient/Task.html @@ -0,0 +1,310 @@ + + + + Task Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Task

+
+
+ +
public final class Task<Response>
+ +
+
+

Response execution context. Will be created by the library and could be used for obtaining +EventLoopFuture<Response> of the execution or cancellation of the execution.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + eventLoop + +
    +
    +
    +
    +
    +
    +

    The EventLoop the delegate will be executed on.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let eventLoop: EventLoop
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + futureResult + +
    +
    +
    +
    +
    +
    +

    EventLoopFuture for the response returned by this request.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var futureResult: EventLoopFuture<Response> { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + wait() + +
    +
    +
    +
    +
    +
    +

    Waits for execution of this request to complete.

    +
    +

    Throws

    + The error value of the EventLoopFuture if it errors. + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func wait() throws -> Response
    + +
    +
    +
    +

    Return Value

    +

    The value of the EventLoopFuture when it completes.

    +
    + +
    +
    +
  • +
  • +
    + + + + cancel() + +
    +
    +
    +
    +
    +
    +

    Cancels the request execution.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func cancel()
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html new file mode 100644 index 000000000..b05decb48 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html @@ -0,0 +1,298 @@ + + + + HTTPClientCopyingDelegate Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientCopyingDelegate

+
+
+ +
public final class HTTPClientCopyingDelegate : HTTPClientResponseDelegate
+ +
+
+

Undocumented

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Classes/ResponseAccumulator.html b/docs/1.4.1/AsyncHTTPClient/Classes/ResponseAccumulator.html new file mode 100644 index 000000000..c70285d46 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Classes/ResponseAccumulator.html @@ -0,0 +1,356 @@ + + + + ResponseAccumulator Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

ResponseAccumulator

+
+
+ +
public class ResponseAccumulator : HTTPClientResponseDelegate
+ +
+
+

Undocumented

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Extensions.html b/docs/1.4.1/AsyncHTTPClient/Extensions.html new file mode 100644 index 000000000..549986320 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Extensions.html @@ -0,0 +1,215 @@ + + + + Extensions Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Extensions

+

The following extensions are available globally.

+ +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Extensions/URL.html b/docs/1.4.1/AsyncHTTPClient/Extensions/URL.html new file mode 100644 index 000000000..303052db4 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Extensions/URL.html @@ -0,0 +1,298 @@ + + + + URL Extension Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

URL

+
+
+ +
extension URL
+ +
+
+ +
+
+ +
+
+
+
    +
  • + +
    +
    +
    +
    +
    +

    Initializes a newly created HTTP URL connecting to a unix domain socket path. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “http+unix” scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(httpURLWithSocketPath socketPath: String, uri: String = "/")
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + uri + + +
    +

    The URI path and query that will be sent to the server.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Initializes a newly created HTTPS URL connecting to a unix domain socket path over TLS. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “https+unix” scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(httpsURLWithSocketPath socketPath: String, uri: String = "/")
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + uri + + +
    +

    The URI path and query that will be sent to the server.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Protocols.html b/docs/1.4.1/AsyncHTTPClient/Protocols.html new file mode 100644 index 000000000..549e4efbd --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Protocols.html @@ -0,0 +1,233 @@ + + + + Protocols Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Protocols

+

The following protocols are available globally.

+ +
+
+ +
+
+
+
    +
  • + +
    +
    +
    +
    +
    +

    HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed. +You can implement this protocol if you need fine-grained control over an HTTP request/response, for example, if you want to inspect the response +headers before deciding whether to accept a response body, or if you want to stream your request body. Pass an instance of your conforming +class to the HTTPClient.execute() method and this package will call each delegate method appropriately as the request takes place./

    +

    Backpressure

    + +

    A HTTPClientResponseDelegate can be used to exert backpressure on the server response. This is achieved by way of the futures returned from +didReceiveHead and didReceiveBodyPart. The following functions are part of the “backpressure system” in the delegate:

    + +
      +
    • didReceiveHead
    • +
    • didReceiveBodyPart
    • +
    • didFinishRequest
    • +
    • didReceiveError
    • +
    + +

    The first three methods are strictly exclusive, with that exclusivity managed by the futures returned by didReceiveHead and +didReceiveBodyPart. What this means is that until the returned future is completed, none of these three methods will be called +again. This allows delegates to rate limit the server to a capacity it can manage. didFinishRequest does not return a future, +as we are expecting no more data from the server at this time.

    + +

    didReceiveError is somewhat special: it signals the end of this regime. didRecieveError is not exclusive: it may be called at +any time, even if a returned future is not yet completed. didReceiveError is terminal, meaning that once it has been called none +of these four methods will be called again. This can be used as a signal to abandon all outstanding work.

    +
    +

    Note

    + This delegate is strongly held by the HTTPTaskHandler + for the duration of the Request processing and will be + released together with the HTTPTaskHandler when channel is closed. + Users of the library are not required to keep a reference to the + object that implements this protocol, but may do so if needed. + +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public protocol HTTPClientResponseDelegate : AnyObject
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html b/docs/1.4.1/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html new file mode 100644 index 000000000..121c55bf0 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html @@ -0,0 +1,715 @@ + + + + HTTPClientResponseDelegate Protocol Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientResponseDelegate

+
+
+ +
public protocol HTTPClientResponseDelegate : AnyObject
+ +
+
+

HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed. +You can implement this protocol if you need fine-grained control over an HTTP request/response, for example, if you want to inspect the response +headers before deciding whether to accept a response body, or if you want to stream your request body. Pass an instance of your conforming +class to the HTTPClient.execute() method and this package will call each delegate method appropriately as the request takes place./

+

Backpressure

+ +

A HTTPClientResponseDelegate can be used to exert backpressure on the server response. This is achieved by way of the futures returned from +didReceiveHead and didReceiveBodyPart. The following functions are part of the “backpressure system” in the delegate:

+ +
    +
  • didReceiveHead
  • +
  • didReceiveBodyPart
  • +
  • didFinishRequest
  • +
  • didReceiveError
  • +
+ +

The first three methods are strictly exclusive, with that exclusivity managed by the futures returned by didReceiveHead and +didReceiveBodyPart. What this means is that until the returned future is completed, none of these three methods will be called +again. This allows delegates to rate limit the server to a capacity it can manage. didFinishRequest does not return a future, +as we are expecting no more data from the server at this time.

+ +

didReceiveError is somewhat special: it signals the end of this regime. didRecieveError is not exclusive: it may be called at +any time, even if a returned future is not yet completed. didReceiveError is terminal, meaning that once it has been called none +of these four methods will be called again. This can be used as a signal to abandon all outstanding work.

+
+

Note

+ This delegate is strongly held by the HTTPTaskHandler + for the duration of the Request processing and will be + released together with the HTTPTaskHandler when channel is closed. + Users of the library are not required to keep a reference to the + object that implements this protocol, but may do so if needed. + +
+ + +
+
+ +
+
+
+
    +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    associatedtype Response
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequestHead(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when the request head is sent. Will be called once.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequestHead(task: HTTPClient.Task<Response>, _ head: HTTPRequestHead)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + head + + +
    +

    Request head.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequestPart(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when a part of the request body is sent. Could be called zero or more times.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequestPart(task: HTTPClient.Task<Response>, _ part: IOData)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + part + + +
    +

    Request body Part.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequest(task:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when the request is fully sent. Will be called once.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequest(task: HTTPClient.Task<Response>)
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveHead(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when response head is received. Will be called once. +You must return an EventLoopFuture<Void> that you complete when you have finished processing the body part. +You can create an already succeeded future by calling task.eventLoop.makeSucceededFuture(()).

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveHead(task: HTTPClient.Task<Response>, _ head: HTTPResponseHead) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + head + + +
    +

    Received reposonse head.

    +
    +
    +
    +
    +

    Return Value

    +

    EventLoopFuture that will be used for backpressure.

    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveBodyPart(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when part of a response body is received. Could be called zero or more times. +You must return an EventLoopFuture<Void> that you complete when you have finished processing the body part. +You can create an already succeeded future by calling task.eventLoop.makeSucceededFuture(()).

    + +

    This function will not be called until the future returned by didReceiveHead has completed.

    + +

    This function will not be called for subsequent body parts until the previous future returned by a +call to this function completes.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveBodyPart(task: HTTPClient.Task<Response>, _ buffer: ByteBuffer) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + buffer + + +
    +

    Received body Part.

    +
    +
    +
    +
    +

    Return Value

    +

    EventLoopFuture that will be used for backpressure.

    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveError(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when error was thrown during request execution. Will be called zero or one time only. Request processing will be stopped after that.

    + +

    This function may be called at any time: it does not respect the backpressure exerted by didReceiveHead and didReceiveBodyPart. +All outstanding work may be cancelled when this is received. Once called, no further calls will be made to didReceiveHead, didReceiveBodyPart, +or didFinishRequest.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveError(task: HTTPClient.Task<Response>, _ error: Error)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + error + + +
    +

    Error that occured during response processing.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Called when the complete HTTP request is finished. You must return an instance of your Response associated type. Will be called once, except if an error occurred.

    + +

    This function will not be called until all futures returned by didReceiveHead and didReceiveBodyPart have completed. Once called, +no further calls will be made to didReceiveHead, didReceiveBodyPart, or didReceiveError.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didFinishRequest(task: HTTPClient.Task<Response>) throws -> Response
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    +
    +
    +

    Return Value

    +

    Result of processing.

    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Structs.html b/docs/1.4.1/AsyncHTTPClient/Structs.html new file mode 100644 index 000000000..64d25dabb --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Structs.html @@ -0,0 +1,201 @@ + + + + Structures Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Structures

+

The following structures are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + HTTPClientError + +
    +
    +
    +
    +
    +
    +

    Possible client errors.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct HTTPClientError : Error, Equatable, CustomStringConvertible
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/Structs/HTTPClientError.html b/docs/1.4.1/AsyncHTTPClient/Structs/HTTPClientError.html new file mode 100644 index 000000000..79f8a5158 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/Structs/HTTPClientError.html @@ -0,0 +1,899 @@ + + + + HTTPClientError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientError

+
+
+ +
public struct HTTPClientError : Error, Equatable, CustomStringConvertible
+ +
+
+

Possible client errors.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var description: String { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + invalidURL + +
    +
    +
    +
    +
    +
    +

    URL provided is invalid.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let invalidURL: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + emptyHost + +
    +
    +
    +
    +
    +
    +

    URL does not contain host.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let emptyHost: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + missingSocketPath + +
    +
    +
    +
    +
    +
    +

    URL does not contain a socketPath as a host for http(s)+unix shemes.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let missingSocketPath: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + alreadyShutdown + +
    +
    +
    +
    +
    +
    +

    Client is shutdown and cannot be used for new requests.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let alreadyShutdown: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + emptyScheme + +
    +
    +
    +
    +
    +
    +

    URL does not contain scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let emptyScheme: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + unsupportedScheme(_:) + +
    +
    +
    +
    +
    +
    +

    Provided URL scheme is not supported, supported schemes are: http and https

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func unsupportedScheme(_ scheme: String) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + readTimeout + +
    +
    +
    +
    +
    +
    +

    Request timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let readTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Remote connection was closed unexpectedly.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let remoteConnectionClosed: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + cancelled + +
    +
    +
    +
    +
    +
    +

    Request was cancelled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let cancelled: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Request contains invalid identity encoding.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let identityCodingIncorrectlyPresent: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Request contains multiple chunks definitions.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let chunkedSpecifiedMultipleTimes: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + invalidProxyResponse + +
    +
    +
    +
    +
    +
    +

    Proxy response was invalid.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let invalidProxyResponse: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + contentLengthMissing + +
    +
    +
    +
    +
    +
    +

    Request does not contain Content-Length header.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let contentLengthMissing: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Proxy Authentication Required.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let proxyAuthenticationRequired: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + redirectLimitReached + +
    +
    +
    +
    +
    +
    +

    Redirect Limit reached.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let redirectLimitReached: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + redirectCycleDetected + +
    +
    +
    +
    +
    +
    +

    Redirect Cycle detected.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let redirectCycleDetected: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + uncleanShutdown + +
    +
    +
    +
    +
    +
    +

    Unclean shutdown.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let uncleanShutdown: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + traceRequestWithBody + +
    +
    +
    +
    +
    +
    +

    A body was sent in a request with method TRACE.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let traceRequestWithBody: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Header field names contain invalid characters.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func invalidHeaderFieldNames(_ names: [String]) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Header field values contain invalid characters.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func invalidHeaderFieldValues(_ values: [String]) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + bodyLengthMismatch + +
    +
    +
    +
    +
    +
    +

    Body length is not equal to Content-Length.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let bodyLengthMismatch: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + writeAfterRequestSent + +
    +
    +
    +
    +
    +
    +

    Body part was written after request was fully sent.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let writeAfterRequestSent: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + incompatibleHeaders + +
    +
    +
    +
    +
    +
    +

    Incompatible headers specified, for example Transfer-Encoding and Content-Length.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let incompatibleHeaders: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/badge.svg b/docs/1.4.1/AsyncHTTPClient/badge.svg new file mode 100644 index 000000000..b28dab66c --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/badge.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + documentation + + + documentation + + + 89% + + + 89% + + + diff --git a/docs/1.4.1/AsyncHTTPClient/css/highlight.css b/docs/1.4.1/AsyncHTTPClient/css/highlight.css new file mode 100644 index 000000000..c170357ce --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/css/highlight.css @@ -0,0 +1,202 @@ +/*! Jazzy - https://github.com/realm/jazzy + * Copyright Realm Inc. + * SPDX-License-Identifier: MIT + */ +/* Credit to https://gist.github.com/wataru420/2048287 */ +.highlight .c { + color: #999988; + font-style: italic; } + +.highlight .err { + color: #a61717; + background-color: #e3d2d2; } + +.highlight .k { + color: #000000; + font-weight: bold; } + +.highlight .o { + color: #000000; + font-weight: bold; } + +.highlight .cm { + color: #999988; + font-style: italic; } + +.highlight .cp { + color: #999999; + font-weight: bold; } + +.highlight .c1 { + color: #999988; + font-style: italic; } + +.highlight .cs { + color: #999999; + font-weight: bold; + font-style: italic; } + +.highlight .gd { + color: #000000; + background-color: #ffdddd; } + +.highlight .gd .x { + color: #000000; + background-color: #ffaaaa; } + +.highlight .ge { + color: #000000; + font-style: italic; } + +.highlight .gr { + color: #aa0000; } + +.highlight .gh { + color: #999999; } + +.highlight .gi { + color: #000000; + background-color: #ddffdd; } + +.highlight .gi .x { + color: #000000; + background-color: #aaffaa; } + +.highlight .go { + color: #888888; } + +.highlight .gp { + color: #555555; } + +.highlight .gs { + font-weight: bold; } + +.highlight .gu { + color: #aaaaaa; } + +.highlight .gt { + color: #aa0000; } + +.highlight .kc { + color: #000000; + font-weight: bold; } + +.highlight .kd { + color: #000000; + font-weight: bold; } + +.highlight .kp { + color: #000000; + font-weight: bold; } + +.highlight .kr { + color: #000000; + font-weight: bold; } + +.highlight .kt { + color: #445588; } + +.highlight .m { + color: #009999; } + +.highlight .s { + color: #d14; } + +.highlight .na { + color: #008080; } + +.highlight .nb { + color: #0086B3; } + +.highlight .nc { + color: #445588; + font-weight: bold; } + +.highlight .no { + color: #008080; } + +.highlight .ni { + color: #800080; } + +.highlight .ne { + color: #990000; + font-weight: bold; } + +.highlight .nf { + color: #990000; } + +.highlight .nn { + color: #555555; } + +.highlight .nt { + color: #000080; } + +.highlight .nv { + color: #008080; } + +.highlight .ow { + color: #000000; + font-weight: bold; } + +.highlight .w { + color: #bbbbbb; } + +.highlight .mf { + color: #009999; } + +.highlight .mh { + color: #009999; } + +.highlight .mi { + color: #009999; } + +.highlight .mo { + color: #009999; } + +.highlight .sb { + color: #d14; } + +.highlight .sc { + color: #d14; } + +.highlight .sd { + color: #d14; } + +.highlight .s2 { + color: #d14; } + +.highlight .se { + color: #d14; } + +.highlight .sh { + color: #d14; } + +.highlight .si { + color: #d14; } + +.highlight .sx { + color: #d14; } + +.highlight .sr { + color: #009926; } + +.highlight .s1 { + color: #d14; } + +.highlight .ss { + color: #990073; } + +.highlight .bp { + color: #999999; } + +.highlight .vc { + color: #008080; } + +.highlight .vg { + color: #008080; } + +.highlight .vi { + color: #008080; } + +.highlight .il { + color: #009999; } diff --git a/docs/1.4.1/AsyncHTTPClient/css/jazzy.css b/docs/1.4.1/AsyncHTTPClient/css/jazzy.css new file mode 100644 index 000000000..c7bb9fe22 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/css/jazzy.css @@ -0,0 +1,404 @@ +/*! Jazzy - https://github.com/realm/jazzy + * Copyright Realm Inc. + * SPDX-License-Identifier: MIT + */ +*, *:before, *:after { + box-sizing: inherit; } + +body { + margin: 0; + background: #fff; + color: #333; + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + letter-spacing: .2px; + -webkit-font-smoothing: antialiased; + box-sizing: border-box; } + +h1 { + font-size: 2rem; + font-weight: 700; + margin: 1.275em 0 0.6em; } + +h2 { + font-size: 1.75rem; + font-weight: 700; + margin: 1.275em 0 0.3em; } + +h3 { + font-size: 1.5rem; + font-weight: 700; + margin: 1em 0 0.3em; } + +h4 { + font-size: 1.25rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h5 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h6 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; + color: #777; } + +p { + margin: 0 0 1em; } + +ul, ol { + padding: 0 0 0 2em; + margin: 0 0 0.85em; } + +blockquote { + margin: 0 0 0.85em; + padding: 0 15px; + color: #858585; + border-left: 4px solid #e5e5e5; } + +img { + max-width: 100%; } + +a { + color: #4183c4; + text-decoration: none; } + a:hover, a:focus { + outline: 0; + text-decoration: underline; } + a.discouraged { + text-decoration: line-through; } + a.discouraged:hover, a.discouraged:focus { + text-decoration: underline line-through; } + +table { + background: #fff; + width: 100%; + border-collapse: collapse; + border-spacing: 0; + overflow: auto; + margin: 0 0 0.85em; } + +tr:nth-child(2n) { + background-color: #fbfbfb; } + +th, td { + padding: 6px 13px; + border: 1px solid #ddd; } + +hr { + height: 1px; + border: none; + background-color: #ddd; } + +pre { + margin: 0 0 1.275em; + padding: .85em 1em; + overflow: auto; + background: #f7f7f7; + font-size: .85em; + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +code { + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +.item-container p > code, .item-container li > code, .top-matter p > code, .top-matter li > code { + background: #f7f7f7; + padding: .2em; } + .item-container p > code:before, .item-container p > code:after, .item-container li > code:before, .item-container li > code:after, .top-matter p > code:before, .top-matter p > code:after, .top-matter li > code:before, .top-matter li > code:after { + letter-spacing: -.2em; + content: "\00a0"; } + +pre code { + padding: 0; + white-space: pre; } + +.content-wrapper { + display: flex; + flex-direction: column; } + @media (min-width: 768px) { + .content-wrapper { + flex-direction: row; } } +.header { + display: flex; + padding: 8px; + font-size: 0.875em; + background: #444; + color: #999; } + +.header-col { + margin: 0; + padding: 0 8px; } + +.header-col--primary { + flex: 1; } + +.header-link { + color: #fff; } + +.header-icon { + padding-right: 2px; + vertical-align: -3px; + height: 16px; } + +.breadcrumbs { + font-size: 0.875em; + padding: 8px 16px; + margin: 0; + background: #fbfbfb; + border-bottom: 1px solid #ddd; } + +.carat { + height: 10px; + margin: 0 5px; } + +.navigation { + order: 2; } + @media (min-width: 768px) { + .navigation { + order: 1; + width: 25%; + max-width: 300px; + padding-bottom: 64px; + overflow: hidden; + word-wrap: normal; + background: #fbfbfb; + border-right: 1px solid #ddd; } } +.nav-groups { + list-style-type: none; + padding-left: 0; } + +.nav-group-name { + border-bottom: 1px solid #ddd; + padding: 8px 0 8px 16px; } + +.nav-group-name-link { + color: #333; } + +.nav-group-tasks { + margin: 8px 0; + padding: 0 0 0 8px; } + +.nav-group-task { + font-size: 1em; + list-style-type: none; + white-space: nowrap; } + +.nav-group-task-link { + color: #808080; } + +.main-content { + order: 1; } + @media (min-width: 768px) { + .main-content { + order: 2; + flex: 1; + padding-bottom: 60px; } } +.section { + padding: 0 32px; + border-bottom: 1px solid #ddd; } + +.section-content { + max-width: 834px; + margin: 0 auto; + padding: 16px 0; } + +.section-name { + color: #666; + display: block; } + .section-name p { + margin-bottom: inherit; } + +.declaration .highlight { + overflow-x: initial; + padding: 8px 0; + margin: 0; + background-color: transparent; + border: none; } + +.task-group-section { + border-top: 1px solid #ddd; } + +.task-group { + padding-top: 0px; } + +.task-name-container a[name]:before { + content: ""; + display: block; } + +.section-name-container { + position: relative; } + .section-name-container .section-name-link { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + margin-bottom: 0; } + .section-name-container .section-name { + position: relative; + pointer-events: none; + z-index: 1; } + .section-name-container .section-name a { + pointer-events: auto; } + +.item-container { + padding: 0; } + +.item { + padding-top: 8px; + width: 100%; + list-style-type: none; } + .item a[name]:before { + content: ""; + display: block; } + .item .token, .item .direct-link { + display: inline-block; + text-indent: -20px; + padding-left: 3px; + margin-left: 20px; + font-size: 1rem; } + .item .declaration-note { + font-size: .85em; + color: #808080; + font-style: italic; } + +.pointer-container { + border-bottom: 1px solid #ddd; + left: -23px; + padding-bottom: 13px; + position: relative; + width: 110%; } + +.pointer { + left: 21px; + top: 7px; + display: block; + position: absolute; + width: 12px; + height: 12px; + border-left: 1px solid #ddd; + border-top: 1px solid #ddd; + background: #fff; + transform: rotate(45deg); } + +.height-container { + display: none; + position: relative; + width: 100%; + overflow: hidden; } + .height-container .section { + background: #fff; + border: 1px solid #ddd; + border-top-width: 0; + padding-top: 10px; + padding-bottom: 5px; + padding: 8px 16px; } + +.aside, .language { + padding: 6px 12px; + margin: 12px 0; + border-left: 5px solid #dddddd; + overflow-y: hidden; } + .aside .aside-title, .language .aside-title { + font-size: 9px; + letter-spacing: 2px; + text-transform: uppercase; + padding-bottom: 0; + margin: 0; + color: #aaa; + -webkit-user-select: none; } + .aside p:last-child, .language p:last-child { + margin-bottom: 0; } + +.language { + border-left: 5px solid #cde9f4; } + .language .aside-title { + color: #4183c4; } + +.aside-warning, .aside-deprecated, .aside-unavailable { + border-left: 5px solid #ff6666; } + .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { + color: #ff0000; } + +.graybox { + border-collapse: collapse; + width: 100%; } + .graybox p { + margin: 0; + word-break: break-word; + min-width: 50px; } + .graybox td { + border: 1px solid #ddd; + padding: 5px 25px 5px 10px; + vertical-align: middle; } + .graybox tr td:first-of-type { + text-align: right; + padding: 7px; + vertical-align: top; + word-break: normal; + width: 40px; } + +.slightly-smaller { + font-size: 0.9em; } + +.footer { + padding: 8px 16px; + background: #444; + color: #ddd; + font-size: 0.8em; } + .footer p { + margin: 8px 0; } + .footer a { + color: #fff; } + +html.dash .header, html.dash .breadcrumbs, html.dash .navigation { + display: none; } + +html.dash .height-container { + display: block; } + +form[role=search] input { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 24px; + padding: 0 10px; + margin: 0; + border: none; + border-radius: 1em; } + .loading form[role=search] input { + background: white url(http://github.com/img/spinner.gif) center right 4px no-repeat; } + +form[role=search] .tt-menu { + margin: 0; + min-width: 300px; + background: #fbfbfb; + color: #333; + border: 1px solid #ddd; } + +form[role=search] .tt-highlight { + font-weight: bold; } + +form[role=search] .tt-suggestion { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 0 8px; } + form[role=search] .tt-suggestion span { + display: table-cell; + white-space: nowrap; } + form[role=search] .tt-suggestion .doc-parent-name { + width: 100%; + text-align: right; + font-weight: normal; + font-size: 0.9em; + padding-left: 16px; } + +form[role=search] .tt-suggestion:hover, +form[role=search] .tt-suggestion.tt-cursor { + cursor: pointer; + background-color: #4183c4; + color: #fff; } + +form[role=search] .tt-suggestion:hover .doc-parent-name, +form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { + color: #fff; } diff --git a/docs/1.4.1/AsyncHTTPClient/img/carat.png b/docs/1.4.1/AsyncHTTPClient/img/carat.png new file mode 100755 index 000000000..29d2f7fd4 Binary files /dev/null and b/docs/1.4.1/AsyncHTTPClient/img/carat.png differ diff --git a/docs/1.4.1/AsyncHTTPClient/img/dash.png b/docs/1.4.1/AsyncHTTPClient/img/dash.png new file mode 100755 index 000000000..6f694c7a0 Binary files /dev/null and b/docs/1.4.1/AsyncHTTPClient/img/dash.png differ diff --git a/docs/1.4.1/AsyncHTTPClient/img/gh.png b/docs/1.4.1/AsyncHTTPClient/img/gh.png new file mode 100755 index 000000000..628da97c7 Binary files /dev/null and b/docs/1.4.1/AsyncHTTPClient/img/gh.png differ diff --git a/docs/1.4.1/AsyncHTTPClient/img/spinner.gif b/docs/1.4.1/AsyncHTTPClient/img/spinner.gif new file mode 100644 index 000000000..e3038d0a4 Binary files /dev/null and b/docs/1.4.1/AsyncHTTPClient/img/spinner.gif differ diff --git a/docs/1.4.1/AsyncHTTPClient/index.html b/docs/1.4.1/AsyncHTTPClient/index.html new file mode 100644 index 000000000..3762fc0e5 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/index.html @@ -0,0 +1,167 @@ + + + + AsyncHTTPClient Reference + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+ +

AsyncHTTPClient Docs

+ +

AsyncHTTPClient is a Swift HTTP Client package.

+ +

To get started with AsyncHTTPClient, import AsyncHTTPClient. The +most important type is HTTPClient +which you can use to emit log messages.

+ +
+
+ + +
+
+ + + diff --git a/docs/1.4.1/AsyncHTTPClient/js/jazzy.js b/docs/1.4.1/AsyncHTTPClient/js/jazzy.js new file mode 100755 index 000000000..198441660 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/js/jazzy.js @@ -0,0 +1,74 @@ +// Jazzy - https://github.com/realm/jazzy +// Copyright Realm Inc. +// SPDX-License-Identifier: MIT + +window.jazzy = {'docset': false} +if (typeof window.dash != 'undefined') { + document.documentElement.className += ' dash' + window.jazzy.docset = true +} +if (navigator.userAgent.match(/xcode/i)) { + document.documentElement.className += ' xcode' + window.jazzy.docset = true +} + +function toggleItem($link, $content) { + var animationDuration = 300; + $link.toggleClass('token-open'); + $content.slideToggle(animationDuration); +} + +function itemLinkToContent($link) { + return $link.parent().parent().next(); +} + +// On doc load + hash-change, open any targetted item +function openCurrentItemIfClosed() { + if (window.jazzy.docset) { + return; + } + var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); + $content = itemLinkToContent($link); + if ($content.is(':hidden')) { + toggleItem($link, $content); + } +} + +$(openCurrentItemIfClosed); +$(window).on('hashchange', openCurrentItemIfClosed); + +// On item link ('token') click, toggle its discussion +$('.token').on('click', function(event) { + if (window.jazzy.docset) { + return; + } + var $link = $(this); + toggleItem($link, itemLinkToContent($link)); + + // Keeps the document from jumping to the hash. + var href = $link.attr('href'); + if (history.pushState) { + history.pushState({}, '', href); + } else { + location.hash = href; + } + event.preventDefault(); +}); + +// Clicks on links to the current, closed, item need to open the item +$("a:not('.token')").on('click', function() { + if (location == this.href) { + openCurrentItemIfClosed(); + } +}); + +// KaTeX rendering +if ("katex" in window) { + $($('.math').each( (_, element) => { + katex.render(element.textContent, element, { + displayMode: $(element).hasClass('m-block'), + throwOnError: false, + trust: true + }); + })) +} diff --git a/docs/1.4.1/AsyncHTTPClient/js/jazzy.search.js b/docs/1.4.1/AsyncHTTPClient/js/jazzy.search.js new file mode 100644 index 000000000..359cdbb8b --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/js/jazzy.search.js @@ -0,0 +1,74 @@ +// Jazzy - https://github.com/realm/jazzy +// Copyright Realm Inc. +// SPDX-License-Identifier: MIT + +$(function(){ + var $typeahead = $('[data-typeahead]'); + var $form = $typeahead.parents('form'); + var searchURL = $form.attr('action'); + + function displayTemplate(result) { + return result.name; + } + + function suggestionTemplate(result) { + var t = '
'; + t += '' + result.name + ''; + if (result.parent_name) { + t += '' + result.parent_name + ''; + } + t += '
'; + return t; + } + + $typeahead.one('focus', function() { + $form.addClass('loading'); + + $.getJSON(searchURL).then(function(searchData) { + const searchIndex = lunr(function() { + this.ref('url'); + this.field('name'); + this.field('abstract'); + for (const [url, doc] of Object.entries(searchData)) { + this.add({url: url, name: doc.name, abstract: doc.abstract}); + } + }); + + $typeahead.typeahead( + { + highlight: true, + minLength: 3, + autoselect: true + }, + { + limit: 10, + display: displayTemplate, + templates: { suggestion: suggestionTemplate }, + source: function(query, sync) { + const lcSearch = query.toLowerCase(); + const results = searchIndex.query(function(q) { + q.term(lcSearch, { boost: 100 }); + q.term(lcSearch, { + boost: 10, + wildcard: lunr.Query.wildcard.TRAILING + }); + }).map(function(result) { + var doc = searchData[result.ref]; + doc.url = result.ref; + return doc; + }); + sync(results); + } + } + ); + $form.removeClass('loading'); + $typeahead.trigger('focus'); + }); + }); + + var baseURL = searchURL.slice(0, -"search.json".length); + + $typeahead.on('typeahead:select', function(e, result) { + window.location = baseURL + result.url; + }); +}); diff --git a/docs/1.4.1/AsyncHTTPClient/js/jquery.min.js b/docs/1.4.1/AsyncHTTPClient/js/jquery.min.js new file mode 100644 index 000000000..2c69bc908 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.1 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,y=n.hasOwnProperty,a=y.toString,l=a.call(Object),v={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&v(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!y||!y.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ve(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ye(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ve(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],y=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||y.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||y.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||y.push(".#.+[+~]"),e.querySelectorAll("\\\f"),y.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),y=y.length&&new RegExp(y.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),v=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&v(p,e)?-1:t==C||t.ownerDocument==p&&v(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!y||!y.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),v.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",v.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",v.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),v.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(v.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return B(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=_e(v.pixelPosition,function(e,t){if(t)return t=Be(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return B(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 00){var c=e.utils.clone(r)||{};c.position=[a,l],c.index=s.length,s.push(new e.Token(i.slice(a,o),c))}a=o+1}}return s},e.tokenizer.separator=/[\s\-]+/,e.Pipeline=function(){this._stack=[]},e.Pipeline.registeredFunctions=Object.create(null),e.Pipeline.registerFunction=function(t,r){r in this.registeredFunctions&&e.utils.warn("Overwriting existing registered function: "+r),t.label=r,e.Pipeline.registeredFunctions[t.label]=t},e.Pipeline.warnIfFunctionNotRegistered=function(t){var r=t.label&&t.label in this.registeredFunctions;r||e.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",t)},e.Pipeline.load=function(t){var r=new e.Pipeline;return t.forEach(function(t){var i=e.Pipeline.registeredFunctions[t];if(!i)throw new Error("Cannot load unregistered function: "+t);r.add(i)}),r},e.Pipeline.prototype.add=function(){var t=Array.prototype.slice.call(arguments);t.forEach(function(t){e.Pipeline.warnIfFunctionNotRegistered(t),this._stack.push(t)},this)},e.Pipeline.prototype.after=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,r)},e.Pipeline.prototype.before=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");this._stack.splice(i,0,r)},e.Pipeline.prototype.remove=function(e){var t=this._stack.indexOf(e);t!=-1&&this._stack.splice(t,1)},e.Pipeline.prototype.run=function(e){for(var t=this._stack.length,r=0;r1&&(se&&(r=n),s!=e);)i=r-t,n=t+Math.floor(i/2),s=this.elements[2*n];return s==e?2*n:s>e?2*n:sa?l+=2:o==a&&(t+=r[u+1]*i[l+1],u+=2,l+=2);return t},e.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},e.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),t=1,r=0;t0){var o,a=s.str.charAt(0);a in s.node.edges?o=s.node.edges[a]:(o=new e.TokenSet,s.node.edges[a]=o),1==s.str.length&&(o["final"]=!0),n.push({node:o,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(0!=s.editsRemaining){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new e.TokenSet;s.node.edges["*"]=u}if(0==s.str.length&&(u["final"]=!0),n.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&n.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),1==s.str.length&&(s.node["final"]=!0),s.str.length>=1){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new e.TokenSet;s.node.edges["*"]=l}1==s.str.length&&(l["final"]=!0),n.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var c,h=s.str.charAt(0),d=s.str.charAt(1);d in s.node.edges?c=s.node.edges[d]:(c=new e.TokenSet,s.node.edges[d]=c),1==s.str.length&&(c["final"]=!0),n.push({node:c,editsRemaining:s.editsRemaining-1,str:h+s.str.slice(2)})}}}return i},e.TokenSet.fromString=function(t){for(var r=new e.TokenSet,i=r,n=0,s=t.length;n=e;t--){var r=this.uncheckedNodes[t],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r["char"]]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}},e.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},e.Index.prototype.search=function(t){return this.query(function(r){var i=new e.QueryParser(t,r);i.parse()})},e.Index.prototype.query=function(t){for(var r=new e.Query(this.fields),i=Object.create(null),n=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),u=0;u1?this._b=1:this._b=e},e.Builder.prototype.k1=function(e){this._k1=e},e.Builder.prototype.add=function(t,r){var i=t[this._ref],n=Object.keys(this._fields);this._documents[i]=r||{},this.documentCount+=1;for(var s=0;s=this.length)return e.QueryLexer.EOS;var t=this.str.charAt(this.pos);return this.pos+=1,t},e.QueryLexer.prototype.width=function(){return this.pos-this.start},e.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},e.QueryLexer.prototype.backup=function(){this.pos-=1},e.QueryLexer.prototype.acceptDigitRun=function(){var t,r;do t=this.next(),r=t.charCodeAt(0);while(r>47&&r<58);t!=e.QueryLexer.EOS&&this.backup()},e.QueryLexer.prototype.more=function(){return this.pos1&&(t.backup(),t.emit(e.QueryLexer.TERM)),t.ignore(),t.more())return e.QueryLexer.lexText},e.QueryLexer.lexEditDistance=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.EDIT_DISTANCE),e.QueryLexer.lexText},e.QueryLexer.lexBoost=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.BOOST),e.QueryLexer.lexText},e.QueryLexer.lexEOS=function(t){t.width()>0&&t.emit(e.QueryLexer.TERM)},e.QueryLexer.termSeparator=e.tokenizer.separator,e.QueryLexer.lexText=function(t){for(;;){var r=t.next();if(r==e.QueryLexer.EOS)return e.QueryLexer.lexEOS;if(92!=r.charCodeAt(0)){if(":"==r)return e.QueryLexer.lexField;if("~"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexEditDistance;if("^"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexBoost;if("+"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if("-"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if(r.match(e.QueryLexer.termSeparator))return e.QueryLexer.lexTerm}else t.escapeCharacter()}},e.QueryParser=function(t,r){this.lexer=new e.QueryLexer(t),this.query=r,this.currentClause={},this.lexemeIdx=0},e.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var t=e.QueryParser.parseClause;t;)t=t(this);return this.query},e.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},e.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},e.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},e.QueryParser.parseClause=function(t){var r=t.peekLexeme();if(void 0!=r)switch(r.type){case e.QueryLexer.PRESENCE:return e.QueryParser.parsePresence;case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expected either a field or a term, found "+r.type;throw r.str.length>=1&&(i+=" with value '"+r.str+"'"),new e.QueryParseError(i,r.start,r.end)}},e.QueryParser.parsePresence=function(t){var r=t.consumeLexeme();if(void 0!=r){switch(r.str){case"-":t.currentClause.presence=e.Query.presence.PROHIBITED;break;case"+":t.currentClause.presence=e.Query.presence.REQUIRED;break;default:var i="unrecognised presence operator'"+r.str+"'";throw new e.QueryParseError(i,r.start,r.end)}var n=t.peekLexeme();if(void 0==n){var i="expecting term or field, found nothing";throw new e.QueryParseError(i,r.start,r.end)}switch(n.type){case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expecting term or field, found '"+n.type+"'";throw new e.QueryParseError(i,n.start,n.end)}}},e.QueryParser.parseField=function(t){var r=t.consumeLexeme();if(void 0!=r){if(t.query.allFields.indexOf(r.str)==-1){var i=t.query.allFields.map(function(e){return"'"+e+"'"}).join(", "),n="unrecognised field '"+r.str+"', possible fields: "+i;throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.fields=[r.str];var s=t.peekLexeme();if(void 0==s){var n="expecting term, found nothing";throw new e.QueryParseError(n,r.start,r.end)}switch(s.type){case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var n="expecting term, found '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseTerm=function(t){var r=t.consumeLexeme();if(void 0!=r){t.currentClause.term=r.str.toLowerCase(),r.str.indexOf("*")!=-1&&(t.currentClause.usePipeline=!1);var i=t.peekLexeme();if(void 0==i)return void t.nextClause();switch(i.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+i.type+"'";throw new e.QueryParseError(n,i.start,i.end)}}},e.QueryParser.parseEditDistance=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="edit distance must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.editDistance=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseBoost=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="boost must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.boost=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():e.lunr=t()}(this,function(){return e})}(); diff --git a/docs/1.4.1/AsyncHTTPClient/js/typeahead.jquery.js b/docs/1.4.1/AsyncHTTPClient/js/typeahead.jquery.js new file mode 100644 index 000000000..3a2d2ab03 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/js/typeahead.jquery.js @@ -0,0 +1,1694 @@ +/*! + * typeahead.js 1.3.1 + * https://github.com/corejavascript/typeahead.js + * Copyright 2013-2020 Twitter, Inc. and other contributors; Licensed MIT + */ + + +(function(root, factory) { + if (typeof define === "function" && define.amd) { + define([ "jquery" ], function(a0) { + return factory(a0); + }); + } else if (typeof module === "object" && module.exports) { + module.exports = factory(require("jquery")); + } else { + factory(root["jQuery"]); + } +})(this, function($) { + var _ = function() { + "use strict"; + return { + isMsie: function() { + return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false; + }, + isBlankString: function(str) { + return !str || /^\s*$/.test(str); + }, + escapeRegExChars: function(str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + }, + isString: function(obj) { + return typeof obj === "string"; + }, + isNumber: function(obj) { + return typeof obj === "number"; + }, + isArray: $.isArray, + isFunction: $.isFunction, + isObject: $.isPlainObject, + isUndefined: function(obj) { + return typeof obj === "undefined"; + }, + isElement: function(obj) { + return !!(obj && obj.nodeType === 1); + }, + isJQuery: function(obj) { + return obj instanceof $; + }, + toStr: function toStr(s) { + return _.isUndefined(s) || s === null ? "" : s + ""; + }, + bind: $.proxy, + each: function(collection, cb) { + $.each(collection, reverseArgs); + function reverseArgs(index, value) { + return cb(value, index); + } + }, + map: $.map, + filter: $.grep, + every: function(obj, test) { + var result = true; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (!(result = test.call(null, val, key, obj))) { + return false; + } + }); + return !!result; + }, + some: function(obj, test) { + var result = false; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (result = test.call(null, val, key, obj)) { + return false; + } + }); + return !!result; + }, + mixin: $.extend, + identity: function(x) { + return x; + }, + clone: function(obj) { + return $.extend(true, {}, obj); + }, + getIdGenerator: function() { + var counter = 0; + return function() { + return counter++; + }; + }, + templatify: function templatify(obj) { + return $.isFunction(obj) ? obj : template; + function template() { + return String(obj); + } + }, + defer: function(fn) { + setTimeout(fn, 0); + }, + debounce: function(func, wait, immediate) { + var timeout, result; + return function() { + var context = this, args = arguments, later, callNow; + later = function() { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + } + }; + callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + } + return result; + }; + }, + throttle: function(func, wait) { + var context, args, timeout, result, previous, later; + previous = 0; + later = function() { + previous = new Date(); + timeout = null; + result = func.apply(context, args); + }; + return function() { + var now = new Date(), remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + } else if (!timeout) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }, + stringify: function(val) { + return _.isString(val) ? val : JSON.stringify(val); + }, + guid: function() { + function _p8(s) { + var p = (Math.random().toString(16) + "000000000").substr(2, 8); + return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p; + } + return "tt-" + _p8() + _p8(true) + _p8(true) + _p8(); + }, + noop: function() {} + }; + }(); + var WWW = function() { + "use strict"; + var defaultClassNames = { + wrapper: "twitter-typeahead", + input: "tt-input", + hint: "tt-hint", + menu: "tt-menu", + dataset: "tt-dataset", + suggestion: "tt-suggestion", + selectable: "tt-selectable", + empty: "tt-empty", + open: "tt-open", + cursor: "tt-cursor", + highlight: "tt-highlight" + }; + return build; + function build(o) { + var www, classes; + classes = _.mixin({}, defaultClassNames, o); + www = { + css: buildCss(), + classes: classes, + html: buildHtml(classes), + selectors: buildSelectors(classes) + }; + return { + css: www.css, + html: www.html, + classes: www.classes, + selectors: www.selectors, + mixin: function(o) { + _.mixin(o, www); + } + }; + } + function buildHtml(c) { + return { + wrapper: '', + menu: '
' + }; + } + function buildSelectors(classes) { + var selectors = {}; + _.each(classes, function(v, k) { + selectors[k] = "." + v; + }); + return selectors; + } + function buildCss() { + var css = { + wrapper: { + position: "relative", + display: "inline-block" + }, + hint: { + position: "absolute", + top: "0", + left: "0", + borderColor: "transparent", + boxShadow: "none", + opacity: "1" + }, + input: { + position: "relative", + verticalAlign: "top", + backgroundColor: "transparent" + }, + inputWithNoHint: { + position: "relative", + verticalAlign: "top" + }, + menu: { + position: "absolute", + top: "100%", + left: "0", + zIndex: "100", + display: "none" + }, + ltr: { + left: "0", + right: "auto" + }, + rtl: { + left: "auto", + right: " 0" + } + }; + if (_.isMsie()) { + _.mixin(css.input, { + backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" + }); + } + return css; + } + }(); + var EventBus = function() { + "use strict"; + var namespace, deprecationMap; + namespace = "typeahead:"; + deprecationMap = { + render: "rendered", + cursorchange: "cursorchanged", + select: "selected", + autocomplete: "autocompleted" + }; + function EventBus(o) { + if (!o || !o.el) { + $.error("EventBus initialized without el"); + } + this.$el = $(o.el); + } + _.mixin(EventBus.prototype, { + _trigger: function(type, args) { + var $e = $.Event(namespace + type); + this.$el.trigger.call(this.$el, $e, args || []); + return $e; + }, + before: function(type) { + var args, $e; + args = [].slice.call(arguments, 1); + $e = this._trigger("before" + type, args); + return $e.isDefaultPrevented(); + }, + trigger: function(type) { + var deprecatedType; + this._trigger(type, [].slice.call(arguments, 1)); + if (deprecatedType = deprecationMap[type]) { + this._trigger(deprecatedType, [].slice.call(arguments, 1)); + } + } + }); + return EventBus; + }(); + var EventEmitter = function() { + "use strict"; + var splitter = /\s+/, nextTick = getNextTick(); + return { + onSync: onSync, + onAsync: onAsync, + off: off, + trigger: trigger + }; + function on(method, types, cb, context) { + var type; + if (!cb) { + return this; + } + types = types.split(splitter); + cb = context ? bindContext(cb, context) : cb; + this._callbacks = this._callbacks || {}; + while (type = types.shift()) { + this._callbacks[type] = this._callbacks[type] || { + sync: [], + async: [] + }; + this._callbacks[type][method].push(cb); + } + return this; + } + function onAsync(types, cb, context) { + return on.call(this, "async", types, cb, context); + } + function onSync(types, cb, context) { + return on.call(this, "sync", types, cb, context); + } + function off(types) { + var type; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + while (type = types.shift()) { + delete this._callbacks[type]; + } + return this; + } + function trigger(types) { + var type, callbacks, args, syncFlush, asyncFlush; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + args = [].slice.call(arguments, 1); + while ((type = types.shift()) && (callbacks = this._callbacks[type])) { + syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args)); + asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args)); + syncFlush() && nextTick(asyncFlush); + } + return this; + } + function getFlush(callbacks, context, args) { + return flush; + function flush() { + var cancelled; + for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) { + cancelled = callbacks[i].apply(context, args) === false; + } + return !cancelled; + } + } + function getNextTick() { + var nextTickFn; + if (window.setImmediate) { + nextTickFn = function nextTickSetImmediate(fn) { + setImmediate(function() { + fn(); + }); + }; + } else { + nextTickFn = function nextTickSetTimeout(fn) { + setTimeout(function() { + fn(); + }, 0); + }; + } + return nextTickFn; + } + function bindContext(fn, context) { + return fn.bind ? fn.bind(context) : function() { + fn.apply(context, [].slice.call(arguments, 0)); + }; + } + }(); + var highlight = function(doc) { + "use strict"; + var defaults = { + node: null, + pattern: null, + tagName: "strong", + className: null, + wordsOnly: false, + caseSensitive: false, + diacriticInsensitive: false + }; + var accented = { + A: "[AaªÀ-Åà-åĀ-ąǍǎȀ-ȃȦȧᴬᵃḀḁẚẠ-ảₐ℀℁℻⒜Ⓐⓐ㍱-㍴㎀-㎄㎈㎉㎩-㎯㏂㏊㏟㏿Aa]", + B: "[BbᴮᵇḂ-ḇℬ⒝Ⓑⓑ㍴㎅-㎇㏃㏈㏔㏝Bb]", + C: "[CcÇçĆ-čᶜ℀ℂ℃℅℆ℭⅭⅽ⒞Ⓒⓒ㍶㎈㎉㎝㎠㎤㏄-㏇Cc]", + D: "[DdĎďDŽ-džDZ-dzᴰᵈḊ-ḓⅅⅆⅮⅾ⒟Ⓓⓓ㋏㍲㍷-㍹㎗㎭-㎯㏅㏈Dd]", + E: "[EeÈ-Ëè-ëĒ-ěȄ-ȇȨȩᴱᵉḘ-ḛẸ-ẽₑ℡ℯℰⅇ⒠Ⓔⓔ㉐㋍㋎Ee]", + F: "[FfᶠḞḟ℉ℱ℻⒡Ⓕⓕ㎊-㎌㎙ff-fflFf]", + G: "[GgĜ-ģǦǧǴǵᴳᵍḠḡℊ⒢Ⓖⓖ㋌㋍㎇㎍-㎏㎓㎬㏆㏉㏒㏿Gg]", + H: "[HhĤĥȞȟʰᴴḢ-ḫẖℋ-ℎ⒣Ⓗⓗ㋌㍱㎐-㎔㏊㏋㏗Hh]", + I: "[IiÌ-Ïì-ïĨ-İIJijǏǐȈ-ȋᴵᵢḬḭỈ-ịⁱℐℑℹⅈⅠ-ⅣⅥ-ⅨⅪⅫⅰ-ⅳⅵ-ⅸⅺⅻ⒤Ⓘⓘ㍺㏌㏕fiffiIi]", + J: "[JjIJ-ĵLJ-njǰʲᴶⅉ⒥ⒿⓙⱼJj]", + K: "[KkĶķǨǩᴷᵏḰ-ḵK⒦Ⓚⓚ㎄㎅㎉㎏㎑㎘㎞㎢㎦㎪㎸㎾㏀㏆㏍-㏏Kk]", + L: "[LlĹ-ŀLJ-ljˡᴸḶḷḺ-ḽℒℓ℡Ⅼⅼ⒧Ⓛⓛ㋏㎈㎉㏐-㏓㏕㏖㏿flfflLl]", + M: "[MmᴹᵐḾ-ṃ℠™ℳⅯⅿ⒨Ⓜⓜ㍷-㍹㎃㎆㎎㎒㎖㎙-㎨㎫㎳㎷㎹㎽㎿㏁㏂㏎㏐㏔-㏖㏘㏙㏞㏟Mm]", + N: "[NnÑñŃ-ʼnNJ-njǸǹᴺṄ-ṋⁿℕ№⒩Ⓝⓝ㎁㎋㎚㎱㎵㎻㏌㏑Nn]", + O: "[OoºÒ-Öò-öŌ-őƠơǑǒǪǫȌ-ȏȮȯᴼᵒỌ-ỏₒ℅№ℴ⒪Ⓞⓞ㍵㏇㏒㏖Oo]", + P: "[PpᴾᵖṔ-ṗℙ⒫Ⓟⓟ㉐㍱㍶㎀㎊㎩-㎬㎰㎴㎺㏋㏗-㏚Pp]", + Q: "[Qqℚ⒬Ⓠⓠ㏃Qq]", + R: "[RrŔ-řȐ-ȓʳᴿᵣṘ-ṛṞṟ₨ℛ-ℝ⒭Ⓡⓡ㋍㍴㎭-㎯㏚㏛Rr]", + S: "[SsŚ-šſȘșˢṠ-ṣ₨℁℠⒮Ⓢⓢ㎧㎨㎮-㎳㏛㏜stSs]", + T: "[TtŢ-ťȚțᵀᵗṪ-ṱẗ℡™⒯Ⓣⓣ㉐㋏㎔㏏ſtstTt]", + U: "[UuÙ-Üù-üŨ-ųƯưǓǔȔ-ȗᵁᵘᵤṲ-ṷỤ-ủ℆⒰Ⓤⓤ㍳㍺Uu]", + V: "[VvᵛᵥṼ-ṿⅣ-Ⅷⅳ-ⅷ⒱Ⓥⓥⱽ㋎㍵㎴-㎹㏜㏞Vv]", + W: "[WwŴŵʷᵂẀ-ẉẘ⒲Ⓦⓦ㎺-㎿㏝Ww]", + X: "[XxˣẊ-ẍₓ℻Ⅸ-Ⅻⅸ-ⅻ⒳Ⓧⓧ㏓Xx]", + Y: "[YyÝýÿŶ-ŸȲȳʸẎẏẙỲ-ỹ⒴Ⓨⓨ㏉Yy]", + Z: "[ZzŹ-žDZ-dzᶻẐ-ẕℤℨ⒵Ⓩⓩ㎐-㎔Zz]" + }; + return function hightlight(o) { + var regex; + o = _.mixin({}, defaults, o); + if (!o.node || !o.pattern) { + return; + } + o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; + regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly, o.diacriticInsensitive); + traverse(o.node, hightlightTextNode); + function hightlightTextNode(textNode) { + var match, patternNode, wrapperNode; + if (match = regex.exec(textNode.data)) { + wrapperNode = doc.createElement(o.tagName); + o.className && (wrapperNode.className = o.className); + patternNode = textNode.splitText(match.index); + patternNode.splitText(match[0].length); + wrapperNode.appendChild(patternNode.cloneNode(true)); + textNode.parentNode.replaceChild(wrapperNode, patternNode); + } + return !!match; + } + function traverse(el, hightlightTextNode) { + var childNode, TEXT_NODE_TYPE = 3; + for (var i = 0; i < el.childNodes.length; i++) { + childNode = el.childNodes[i]; + if (childNode.nodeType === TEXT_NODE_TYPE) { + i += hightlightTextNode(childNode) ? 1 : 0; + } else { + traverse(childNode, hightlightTextNode); + } + } + } + }; + function accent_replacer(chr) { + return accented[chr.toUpperCase()] || chr; + } + function getRegex(patterns, caseSensitive, wordsOnly, diacriticInsensitive) { + var escapedPatterns = [], regexStr; + for (var i = 0, len = patterns.length; i < len; i++) { + var escapedWord = _.escapeRegExChars(patterns[i]); + if (diacriticInsensitive) { + escapedWord = escapedWord.replace(/\S/g, accent_replacer); + } + escapedPatterns.push(escapedWord); + } + regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")"; + return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); + } + }(window.document); + var Input = function() { + "use strict"; + var specialKeyCodeMap; + specialKeyCodeMap = { + 9: "tab", + 27: "esc", + 37: "left", + 39: "right", + 13: "enter", + 38: "up", + 40: "down" + }; + function Input(o, www) { + var id; + o = o || {}; + if (!o.input) { + $.error("input is missing"); + } + www.mixin(this); + this.$hint = $(o.hint); + this.$input = $(o.input); + this.$menu = $(o.menu); + id = this.$input.attr("id") || _.guid(); + this.$menu.attr("id", id + "_listbox"); + this.$hint.attr({ + "aria-hidden": true + }); + this.$input.attr({ + "aria-owns": id + "_listbox", + role: "combobox", + "aria-autocomplete": "list", + "aria-expanded": false + }); + this.query = this.$input.val(); + this.queryWhenFocused = this.hasFocus() ? this.query : null; + this.$overflowHelper = buildOverflowHelper(this.$input); + this._checkLanguageDirection(); + if (this.$hint.length === 0) { + this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; + } + this.onSync("cursorchange", this._updateDescendent); + } + Input.normalizeQuery = function(str) { + return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " "); + }; + _.mixin(Input.prototype, EventEmitter, { + _onBlur: function onBlur() { + this.resetInputValue(); + this.trigger("blurred"); + }, + _onFocus: function onFocus() { + this.queryWhenFocused = this.query; + this.trigger("focused"); + }, + _onKeydown: function onKeydown($e) { + var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; + this._managePreventDefault(keyName, $e); + if (keyName && this._shouldTrigger(keyName, $e)) { + this.trigger(keyName + "Keyed", $e); + } + }, + _onInput: function onInput() { + this._setQuery(this.getInputValue()); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + _managePreventDefault: function managePreventDefault(keyName, $e) { + var preventDefault; + switch (keyName) { + case "up": + case "down": + preventDefault = !withModifier($e); + break; + + default: + preventDefault = false; + } + preventDefault && $e.preventDefault(); + }, + _shouldTrigger: function shouldTrigger(keyName, $e) { + var trigger; + switch (keyName) { + case "tab": + trigger = !withModifier($e); + break; + + default: + trigger = true; + } + return trigger; + }, + _checkLanguageDirection: function checkLanguageDirection() { + var dir = (this.$input.css("direction") || "ltr").toLowerCase(); + if (this.dir !== dir) { + this.dir = dir; + this.$hint.attr("dir", dir); + this.trigger("langDirChanged", dir); + } + }, + _setQuery: function setQuery(val, silent) { + var areEquivalent, hasDifferentWhitespace; + areEquivalent = areQueriesEquivalent(val, this.query); + hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false; + this.query = val; + if (!silent && !areEquivalent) { + this.trigger("queryChanged", this.query); + } else if (!silent && hasDifferentWhitespace) { + this.trigger("whitespaceChanged", this.query); + } + }, + _updateDescendent: function updateDescendent(event, id) { + this.$input.attr("aria-activedescendant", id); + }, + bind: function() { + var that = this, onBlur, onFocus, onKeydown, onInput; + onBlur = _.bind(this._onBlur, this); + onFocus = _.bind(this._onFocus, this); + onKeydown = _.bind(this._onKeydown, this); + onInput = _.bind(this._onInput, this); + this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown); + if (!_.isMsie() || _.isMsie() > 9) { + this.$input.on("input.tt", onInput); + } else { + this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { + if (specialKeyCodeMap[$e.which || $e.keyCode]) { + return; + } + _.defer(_.bind(that._onInput, that, $e)); + }); + } + return this; + }, + focus: function focus() { + this.$input.focus(); + }, + blur: function blur() { + this.$input.blur(); + }, + getLangDir: function getLangDir() { + return this.dir; + }, + getQuery: function getQuery() { + return this.query || ""; + }, + setQuery: function setQuery(val, silent) { + this.setInputValue(val); + this._setQuery(val, silent); + }, + hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() { + return this.query !== this.queryWhenFocused; + }, + getInputValue: function getInputValue() { + return this.$input.val(); + }, + setInputValue: function setInputValue(value) { + this.$input.val(value); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + resetInputValue: function resetInputValue() { + this.setInputValue(this.query); + }, + getHint: function getHint() { + return this.$hint.val(); + }, + setHint: function setHint(value) { + this.$hint.val(value); + }, + clearHint: function clearHint() { + this.setHint(""); + }, + clearHintIfInvalid: function clearHintIfInvalid() { + var val, hint, valIsPrefixOfHint, isValid; + val = this.getInputValue(); + hint = this.getHint(); + valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; + isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow(); + !isValid && this.clearHint(); + }, + hasFocus: function hasFocus() { + return this.$input.is(":focus"); + }, + hasOverflow: function hasOverflow() { + var constraint = this.$input.width() - 2; + this.$overflowHelper.text(this.getInputValue()); + return this.$overflowHelper.width() >= constraint; + }, + isCursorAtEnd: function() { + var valueLength, selectionStart, range; + valueLength = this.$input.val().length; + selectionStart = this.$input[0].selectionStart; + if (_.isNumber(selectionStart)) { + return selectionStart === valueLength; + } else if (document.selection) { + range = document.selection.createRange(); + range.moveStart("character", -valueLength); + return valueLength === range.text.length; + } + return true; + }, + destroy: function destroy() { + this.$hint.off(".tt"); + this.$input.off(".tt"); + this.$overflowHelper.remove(); + this.$hint = this.$input = this.$overflowHelper = $("
"); + }, + setAriaExpanded: function setAriaExpanded(value) { + this.$input.attr("aria-expanded", value); + } + }); + return Input; + function buildOverflowHelper($input) { + return $('').css({ + position: "absolute", + visibility: "hidden", + whiteSpace: "pre", + fontFamily: $input.css("font-family"), + fontSize: $input.css("font-size"), + fontStyle: $input.css("font-style"), + fontVariant: $input.css("font-variant"), + fontWeight: $input.css("font-weight"), + wordSpacing: $input.css("word-spacing"), + letterSpacing: $input.css("letter-spacing"), + textIndent: $input.css("text-indent"), + textRendering: $input.css("text-rendering"), + textTransform: $input.css("text-transform") + }).insertAfter($input); + } + function areQueriesEquivalent(a, b) { + return Input.normalizeQuery(a) === Input.normalizeQuery(b); + } + function withModifier($e) { + return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; + } + }(); + var Dataset = function() { + "use strict"; + var keys, nameGenerator; + keys = { + dataset: "tt-selectable-dataset", + val: "tt-selectable-display", + obj: "tt-selectable-object" + }; + nameGenerator = _.getIdGenerator(); + function Dataset(o, www) { + o = o || {}; + o.templates = o.templates || {}; + o.templates.notFound = o.templates.notFound || o.templates.empty; + if (!o.source) { + $.error("missing source"); + } + if (!o.node) { + $.error("missing node"); + } + if (o.name && !isValidName(o.name)) { + $.error("invalid dataset name: " + o.name); + } + www.mixin(this); + this.highlight = !!o.highlight; + this.name = _.toStr(o.name || nameGenerator()); + this.limit = o.limit || 5; + this.displayFn = getDisplayFn(o.display || o.displayKey); + this.templates = getTemplates(o.templates, this.displayFn); + this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source; + this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async; + this._resetLastSuggestion(); + this.$el = $(o.node).attr("role", "presentation").addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); + } + Dataset.extractData = function extractData(el) { + var $el = $(el); + if ($el.data(keys.obj)) { + return { + dataset: $el.data(keys.dataset) || "", + val: $el.data(keys.val) || "", + obj: $el.data(keys.obj) || null + }; + } + return null; + }; + _.mixin(Dataset.prototype, EventEmitter, { + _overwrite: function overwrite(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (this.async && this.templates.pending) { + this._renderPending(query); + } else if (!this.async && this.templates.notFound) { + this._renderNotFound(query); + } else { + this._empty(); + } + this.trigger("rendered", suggestions, false, this.name); + }, + _append: function append(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length && this.$lastSuggestion.length) { + this._appendSuggestions(query, suggestions); + } else if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (!this.$lastSuggestion.length && this.templates.notFound) { + this._renderNotFound(query); + } + this.trigger("rendered", suggestions, true, this.name); + }, + _renderSuggestions: function renderSuggestions(query, suggestions) { + var $fragment; + $fragment = this._getSuggestionsFragment(query, suggestions); + this.$lastSuggestion = $fragment.children().last(); + this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions)); + }, + _appendSuggestions: function appendSuggestions(query, suggestions) { + var $fragment, $lastSuggestion; + $fragment = this._getSuggestionsFragment(query, suggestions); + $lastSuggestion = $fragment.children().last(); + this.$lastSuggestion.after($fragment); + this.$lastSuggestion = $lastSuggestion; + }, + _renderPending: function renderPending(query) { + var template = this.templates.pending; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _renderNotFound: function renderNotFound(query) { + var template = this.templates.notFound; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _empty: function empty() { + this.$el.empty(); + this._resetLastSuggestion(); + }, + _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) { + var that = this, fragment; + fragment = document.createDocumentFragment(); + _.each(suggestions, function getSuggestionNode(suggestion) { + var $el, context; + context = that._injectQuery(query, suggestion); + $el = $(that.templates.suggestion(context)).data(keys.dataset, that.name).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable); + fragment.appendChild($el[0]); + }); + this.highlight && highlight({ + className: this.classes.highlight, + node: fragment, + pattern: query + }); + return $(fragment); + }, + _getFooter: function getFooter(query, suggestions) { + return this.templates.footer ? this.templates.footer({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _getHeader: function getHeader(query, suggestions) { + return this.templates.header ? this.templates.header({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _resetLastSuggestion: function resetLastSuggestion() { + this.$lastSuggestion = $(); + }, + _injectQuery: function injectQuery(query, obj) { + return _.isObject(obj) ? _.mixin({ + _query: query + }, obj) : obj; + }, + update: function update(query) { + var that = this, canceled = false, syncCalled = false, rendered = 0; + this.cancel(); + this.cancel = function cancel() { + canceled = true; + that.cancel = $.noop; + that.async && that.trigger("asyncCanceled", query, that.name); + }; + this.source(query, sync, async); + !syncCalled && sync([]); + function sync(suggestions) { + if (syncCalled) { + return; + } + syncCalled = true; + suggestions = (suggestions || []).slice(0, that.limit); + rendered = suggestions.length; + that._overwrite(query, suggestions); + if (rendered < that.limit && that.async) { + that.trigger("asyncRequested", query, that.name); + } + } + function async(suggestions) { + suggestions = suggestions || []; + if (!canceled && rendered < that.limit) { + that.cancel = $.noop; + var idx = Math.abs(rendered - that.limit); + rendered += idx; + that._append(query, suggestions.slice(0, idx)); + that.async && that.trigger("asyncReceived", query, that.name); + } + } + }, + cancel: $.noop, + clear: function clear() { + this._empty(); + this.cancel(); + this.trigger("cleared"); + }, + isEmpty: function isEmpty() { + return this.$el.is(":empty"); + }, + destroy: function destroy() { + this.$el = $("
"); + } + }); + return Dataset; + function getDisplayFn(display) { + display = display || _.stringify; + return _.isFunction(display) ? display : displayFn; + function displayFn(obj) { + return obj[display]; + } + } + function getTemplates(templates, displayFn) { + return { + notFound: templates.notFound && _.templatify(templates.notFound), + pending: templates.pending && _.templatify(templates.pending), + header: templates.header && _.templatify(templates.header), + footer: templates.footer && _.templatify(templates.footer), + suggestion: templates.suggestion ? userSuggestionTemplate : suggestionTemplate + }; + function userSuggestionTemplate(context) { + var template = templates.suggestion; + return $(template(context)).attr("id", _.guid()); + } + function suggestionTemplate(context) { + return $('
').attr("id", _.guid()).text(displayFn(context)); + } + } + function isValidName(str) { + return /^[_a-zA-Z0-9-]+$/.test(str); + } + }(); + var Menu = function() { + "use strict"; + function Menu(o, www) { + var that = this; + o = o || {}; + if (!o.node) { + $.error("node is required"); + } + www.mixin(this); + this.$node = $(o.node); + this.query = null; + this.datasets = _.map(o.datasets, initializeDataset); + function initializeDataset(oDataset) { + var node = that.$node.find(oDataset.node).first(); + oDataset.node = node.length ? node : $("
").appendTo(that.$node); + return new Dataset(oDataset, www); + } + } + _.mixin(Menu.prototype, EventEmitter, { + _onSelectableClick: function onSelectableClick($e) { + this.trigger("selectableClicked", $($e.currentTarget)); + }, + _onRendered: function onRendered(type, dataset, suggestions, async) { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetRendered", dataset, suggestions, async); + }, + _onCleared: function onCleared() { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetCleared"); + }, + _propagate: function propagate() { + this.trigger.apply(this, arguments); + }, + _allDatasetsEmpty: function allDatasetsEmpty() { + return _.every(this.datasets, _.bind(function isDatasetEmpty(dataset) { + var isEmpty = dataset.isEmpty(); + this.$node.attr("aria-expanded", !isEmpty); + return isEmpty; + }, this)); + }, + _getSelectables: function getSelectables() { + return this.$node.find(this.selectors.selectable); + }, + _removeCursor: function _removeCursor() { + var $selectable = this.getActiveSelectable(); + $selectable && $selectable.removeClass(this.classes.cursor); + }, + _ensureVisible: function ensureVisible($el) { + var elTop, elBottom, nodeScrollTop, nodeHeight; + elTop = $el.position().top; + elBottom = elTop + $el.outerHeight(true); + nodeScrollTop = this.$node.scrollTop(); + nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10); + if (elTop < 0) { + this.$node.scrollTop(nodeScrollTop + elTop); + } else if (nodeHeight < elBottom) { + this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight)); + } + }, + bind: function() { + var that = this, onSelectableClick; + onSelectableClick = _.bind(this._onSelectableClick, this); + this.$node.on("click.tt", this.selectors.selectable, onSelectableClick); + this.$node.on("mouseover", this.selectors.selectable, function() { + that.setCursor($(this)); + }); + this.$node.on("mouseleave", function() { + that._removeCursor(); + }); + _.each(this.datasets, function(dataset) { + dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that); + }); + return this; + }, + isOpen: function isOpen() { + return this.$node.hasClass(this.classes.open); + }, + open: function open() { + this.$node.scrollTop(0); + this.$node.addClass(this.classes.open); + }, + close: function close() { + this.$node.attr("aria-expanded", false); + this.$node.removeClass(this.classes.open); + this._removeCursor(); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.attr("dir", dir); + }, + selectableRelativeToCursor: function selectableRelativeToCursor(delta) { + var $selectables, $oldCursor, oldIndex, newIndex; + $oldCursor = this.getActiveSelectable(); + $selectables = this._getSelectables(); + oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1; + newIndex = oldIndex + delta; + newIndex = (newIndex + 1) % ($selectables.length + 1) - 1; + newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex; + return newIndex === -1 ? null : $selectables.eq(newIndex); + }, + setCursor: function setCursor($selectable) { + this._removeCursor(); + if ($selectable = $selectable && $selectable.first()) { + $selectable.addClass(this.classes.cursor); + this._ensureVisible($selectable); + } + }, + getSelectableData: function getSelectableData($el) { + return $el && $el.length ? Dataset.extractData($el) : null; + }, + getActiveSelectable: function getActiveSelectable() { + var $selectable = this._getSelectables().filter(this.selectors.cursor).first(); + return $selectable.length ? $selectable : null; + }, + getTopSelectable: function getTopSelectable() { + var $selectable = this._getSelectables().first(); + return $selectable.length ? $selectable : null; + }, + update: function update(query) { + var isValidUpdate = query !== this.query; + if (isValidUpdate) { + this.query = query; + _.each(this.datasets, updateDataset); + } + return isValidUpdate; + function updateDataset(dataset) { + dataset.update(query); + } + }, + empty: function empty() { + _.each(this.datasets, clearDataset); + this.query = null; + this.$node.addClass(this.classes.empty); + function clearDataset(dataset) { + dataset.clear(); + } + }, + destroy: function destroy() { + this.$node.off(".tt"); + this.$node = $("
"); + _.each(this.datasets, destroyDataset); + function destroyDataset(dataset) { + dataset.destroy(); + } + } + }); + return Menu; + }(); + var Status = function() { + "use strict"; + function Status(options) { + this.$el = $("", { + role: "status", + "aria-live": "polite" + }).css({ + position: "absolute", + padding: "0", + border: "0", + height: "1px", + width: "1px", + "margin-bottom": "-1px", + "margin-right": "-1px", + overflow: "hidden", + clip: "rect(0 0 0 0)", + "white-space": "nowrap" + }); + options.$input.after(this.$el); + _.each(options.menu.datasets, _.bind(function(dataset) { + if (dataset.onSync) { + dataset.onSync("rendered", _.bind(this.update, this)); + dataset.onSync("cleared", _.bind(this.cleared, this)); + } + }, this)); + } + _.mixin(Status.prototype, { + update: function update(event, suggestions) { + var length = suggestions.length; + var words; + if (length === 1) { + words = { + result: "result", + is: "is" + }; + } else { + words = { + result: "results", + is: "are" + }; + } + this.$el.text(length + " " + words.result + " " + words.is + " available, use up and down arrow keys to navigate."); + }, + cleared: function() { + this.$el.text(""); + } + }); + return Status; + }(); + var DefaultMenu = function() { + "use strict"; + var s = Menu.prototype; + function DefaultMenu() { + Menu.apply(this, [].slice.call(arguments, 0)); + } + _.mixin(DefaultMenu.prototype, Menu.prototype, { + open: function open() { + !this._allDatasetsEmpty() && this._show(); + return s.open.apply(this, [].slice.call(arguments, 0)); + }, + close: function close() { + this._hide(); + return s.close.apply(this, [].slice.call(arguments, 0)); + }, + _onRendered: function onRendered() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onRendered.apply(this, [].slice.call(arguments, 0)); + }, + _onCleared: function onCleared() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onCleared.apply(this, [].slice.call(arguments, 0)); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl); + return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0)); + }, + _hide: function hide() { + this.$node.hide(); + }, + _show: function show() { + this.$node.css("display", "block"); + } + }); + return DefaultMenu; + }(); + var Typeahead = function() { + "use strict"; + function Typeahead(o, www) { + var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged; + o = o || {}; + if (!o.input) { + $.error("missing input"); + } + if (!o.menu) { + $.error("missing menu"); + } + if (!o.eventBus) { + $.error("missing event bus"); + } + www.mixin(this); + this.eventBus = o.eventBus; + this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; + this.input = o.input; + this.menu = o.menu; + this.enabled = true; + this.autoselect = !!o.autoselect; + this.active = false; + this.input.hasFocus() && this.activate(); + this.dir = this.input.getLangDir(); + this._hacks(); + this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this); + onFocused = c(this, "activate", "open", "_onFocused"); + onBlurred = c(this, "deactivate", "_onBlurred"); + onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed"); + onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed"); + onEscKeyed = c(this, "isActive", "_onEscKeyed"); + onUpKeyed = c(this, "isActive", "open", "_onUpKeyed"); + onDownKeyed = c(this, "isActive", "open", "_onDownKeyed"); + onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed"); + onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed"); + onQueryChanged = c(this, "_openIfActive", "_onQueryChanged"); + onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged"); + this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this); + } + _.mixin(Typeahead.prototype, { + _hacks: function hacks() { + var $input, $menu; + $input = this.input.$input || $("
"); + $menu = this.menu.$node || $("
"); + $input.on("blur.tt", function($e) { + var active, isActive, hasActive; + active = document.activeElement; + isActive = $menu.is(active); + hasActive = $menu.has(active).length > 0; + if (_.isMsie() && (isActive || hasActive)) { + $e.preventDefault(); + $e.stopImmediatePropagation(); + _.defer(function() { + $input.focus(); + }); + } + }); + $menu.on("mousedown.tt", function($e) { + $e.preventDefault(); + }); + }, + _onSelectableClicked: function onSelectableClicked(type, $el) { + this.select($el); + }, + _onDatasetCleared: function onDatasetCleared() { + this._updateHint(); + }, + _onDatasetRendered: function onDatasetRendered(type, suggestions, async, dataset) { + this._updateHint(); + if (this.autoselect) { + var cursorClass = this.selectors.cursor.substr(1); + this.menu.$node.find(this.selectors.suggestion).first().addClass(cursorClass); + } + this.eventBus.trigger("render", suggestions, async, dataset); + }, + _onAsyncRequested: function onAsyncRequested(type, dataset, query) { + this.eventBus.trigger("asyncrequest", query, dataset); + }, + _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) { + this.eventBus.trigger("asynccancel", query, dataset); + }, + _onAsyncReceived: function onAsyncReceived(type, dataset, query) { + this.eventBus.trigger("asyncreceive", query, dataset); + }, + _onFocused: function onFocused() { + this._minLengthMet() && this.menu.update(this.input.getQuery()); + }, + _onBlurred: function onBlurred() { + if (this.input.hasQueryChangedSinceLastFocus()) { + this.eventBus.trigger("change", this.input.getQuery()); + } + }, + _onEnterKeyed: function onEnterKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + if (this.select($selectable)) { + $e.preventDefault(); + $e.stopPropagation(); + } + } else if (this.autoselect) { + if (this.select(this.menu.getTopSelectable())) { + $e.preventDefault(); + $e.stopPropagation(); + } + } + }, + _onTabKeyed: function onTabKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + this.select($selectable) && $e.preventDefault(); + } else if (this.autoselect) { + if ($selectable = this.menu.getTopSelectable()) { + this.autocomplete($selectable) && $e.preventDefault(); + } + } + }, + _onEscKeyed: function onEscKeyed() { + this.close(); + }, + _onUpKeyed: function onUpKeyed() { + this.moveCursor(-1); + }, + _onDownKeyed: function onDownKeyed() { + this.moveCursor(+1); + }, + _onLeftKeyed: function onLeftKeyed() { + if (this.dir === "rtl" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); + } + }, + _onRightKeyed: function onRightKeyed() { + if (this.dir === "ltr" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); + } + }, + _onQueryChanged: function onQueryChanged(e, query) { + this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty(); + }, + _onWhitespaceChanged: function onWhitespaceChanged() { + this._updateHint(); + }, + _onLangDirChanged: function onLangDirChanged(e, dir) { + if (this.dir !== dir) { + this.dir = dir; + this.menu.setLanguageDirection(dir); + } + }, + _openIfActive: function openIfActive() { + this.isActive() && this.open(); + }, + _minLengthMet: function minLengthMet(query) { + query = _.isString(query) ? query : this.input.getQuery() || ""; + return query.length >= this.minLength; + }, + _updateHint: function updateHint() { + var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match; + $selectable = this.menu.getTopSelectable(); + data = this.menu.getSelectableData($selectable); + val = this.input.getInputValue(); + if (data && !_.isBlankString(val) && !this.input.hasOverflow()) { + query = Input.normalizeQuery(val); + escapedQuery = _.escapeRegExChars(query); + frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i"); + match = frontMatchRegEx.exec(data.val); + match && this.input.setHint(val + match[1]); + } else { + this.input.clearHint(); + } + }, + isEnabled: function isEnabled() { + return this.enabled; + }, + enable: function enable() { + this.enabled = true; + }, + disable: function disable() { + this.enabled = false; + }, + isActive: function isActive() { + return this.active; + }, + activate: function activate() { + if (this.isActive()) { + return true; + } else if (!this.isEnabled() || this.eventBus.before("active")) { + return false; + } else { + this.active = true; + this.eventBus.trigger("active"); + return true; + } + }, + deactivate: function deactivate() { + if (!this.isActive()) { + return true; + } else if (this.eventBus.before("idle")) { + return false; + } else { + this.active = false; + this.close(); + this.eventBus.trigger("idle"); + return true; + } + }, + isOpen: function isOpen() { + return this.menu.isOpen(); + }, + open: function open() { + if (!this.isOpen() && !this.eventBus.before("open")) { + this.input.setAriaExpanded(true); + this.menu.open(); + this._updateHint(); + this.eventBus.trigger("open"); + } + return this.isOpen(); + }, + close: function close() { + if (this.isOpen() && !this.eventBus.before("close")) { + this.input.setAriaExpanded(false); + this.menu.close(); + this.input.clearHint(); + this.input.resetInputValue(); + this.eventBus.trigger("close"); + } + return !this.isOpen(); + }, + setVal: function setVal(val) { + this.input.setQuery(_.toStr(val)); + }, + getVal: function getVal() { + return this.input.getQuery(); + }, + select: function select($selectable) { + var data = this.menu.getSelectableData($selectable); + if (data && !this.eventBus.before("select", data.obj, data.dataset)) { + this.input.setQuery(data.val, true); + this.eventBus.trigger("select", data.obj, data.dataset); + this.close(); + return true; + } + return false; + }, + autocomplete: function autocomplete($selectable) { + var query, data, isValid; + query = this.input.getQuery(); + data = this.menu.getSelectableData($selectable); + isValid = data && query !== data.val; + if (isValid && !this.eventBus.before("autocomplete", data.obj, data.dataset)) { + this.input.setQuery(data.val); + this.eventBus.trigger("autocomplete", data.obj, data.dataset); + return true; + } + return false; + }, + moveCursor: function moveCursor(delta) { + var query, $candidate, data, suggestion, datasetName, cancelMove, id; + query = this.input.getQuery(); + $candidate = this.menu.selectableRelativeToCursor(delta); + data = this.menu.getSelectableData($candidate); + suggestion = data ? data.obj : null; + datasetName = data ? data.dataset : null; + id = $candidate ? $candidate.attr("id") : null; + this.input.trigger("cursorchange", id); + cancelMove = this._minLengthMet() && this.menu.update(query); + if (!cancelMove && !this.eventBus.before("cursorchange", suggestion, datasetName)) { + this.menu.setCursor($candidate); + if (data) { + if (typeof data.val === "string") { + this.input.setInputValue(data.val); + } + } else { + this.input.resetInputValue(); + this._updateHint(); + } + this.eventBus.trigger("cursorchange", suggestion, datasetName); + return true; + } + return false; + }, + destroy: function destroy() { + this.input.destroy(); + this.menu.destroy(); + } + }); + return Typeahead; + function c(ctx) { + var methods = [].slice.call(arguments, 1); + return function() { + var args = [].slice.call(arguments); + _.each(methods, function(method) { + return ctx[method].apply(ctx, args); + }); + }; + } + }(); + (function() { + "use strict"; + var old, keys, methods; + old = $.fn.typeahead; + keys = { + www: "tt-www", + attrs: "tt-attrs", + typeahead: "tt-typeahead" + }; + methods = { + initialize: function initialize(o, datasets) { + var www; + datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); + o = o || {}; + www = WWW(o.classNames); + return this.each(attach); + function attach() { + var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, status, typeahead, MenuConstructor; + _.each(datasets, function(d) { + d.highlight = !!o.highlight; + }); + $input = $(this); + $wrapper = $(www.html.wrapper); + $hint = $elOrNull(o.hint); + $menu = $elOrNull(o.menu); + defaultHint = o.hint !== false && !$hint; + defaultMenu = o.menu !== false && !$menu; + defaultHint && ($hint = buildHintFromInput($input, www)); + defaultMenu && ($menu = $(www.html.menu).css(www.css.menu)); + $hint && $hint.val(""); + $input = prepInput($input, www); + if (defaultHint || defaultMenu) { + $wrapper.css(www.css.wrapper); + $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint); + $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null); + } + MenuConstructor = defaultMenu ? DefaultMenu : Menu; + eventBus = new EventBus({ + el: $input + }); + input = new Input({ + hint: $hint, + input: $input, + menu: $menu + }, www); + menu = new MenuConstructor({ + node: $menu, + datasets: datasets + }, www); + status = new Status({ + $input: $input, + menu: menu + }); + typeahead = new Typeahead({ + input: input, + menu: menu, + eventBus: eventBus, + minLength: o.minLength, + autoselect: o.autoselect + }, www); + $input.data(keys.www, www); + $input.data(keys.typeahead, typeahead); + } + }, + isEnabled: function isEnabled() { + var enabled; + ttEach(this.first(), function(t) { + enabled = t.isEnabled(); + }); + return enabled; + }, + enable: function enable() { + ttEach(this, function(t) { + t.enable(); + }); + return this; + }, + disable: function disable() { + ttEach(this, function(t) { + t.disable(); + }); + return this; + }, + isActive: function isActive() { + var active; + ttEach(this.first(), function(t) { + active = t.isActive(); + }); + return active; + }, + activate: function activate() { + ttEach(this, function(t) { + t.activate(); + }); + return this; + }, + deactivate: function deactivate() { + ttEach(this, function(t) { + t.deactivate(); + }); + return this; + }, + isOpen: function isOpen() { + var open; + ttEach(this.first(), function(t) { + open = t.isOpen(); + }); + return open; + }, + open: function open() { + ttEach(this, function(t) { + t.open(); + }); + return this; + }, + close: function close() { + ttEach(this, function(t) { + t.close(); + }); + return this; + }, + select: function select(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.select($el); + }); + return success; + }, + autocomplete: function autocomplete(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.autocomplete($el); + }); + return success; + }, + moveCursor: function moveCursoe(delta) { + var success = false; + ttEach(this.first(), function(t) { + success = t.moveCursor(delta); + }); + return success; + }, + val: function val(newVal) { + var query; + if (!arguments.length) { + ttEach(this.first(), function(t) { + query = t.getVal(); + }); + return query; + } else { + ttEach(this, function(t) { + t.setVal(_.toStr(newVal)); + }); + return this; + } + }, + destroy: function destroy() { + ttEach(this, function(typeahead, $input) { + revert($input); + typeahead.destroy(); + }); + return this; + } + }; + $.fn.typeahead = function(method) { + if (methods[method]) { + return methods[method].apply(this, [].slice.call(arguments, 1)); + } else { + return methods.initialize.apply(this, arguments); + } + }; + $.fn.typeahead.noConflict = function noConflict() { + $.fn.typeahead = old; + return this; + }; + function ttEach($els, fn) { + $els.each(function() { + var $input = $(this), typeahead; + (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input); + }); + } + function buildHintFromInput($input, www) { + return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop({ + readonly: true, + required: false + }).removeAttr("id name placeholder").removeClass("required").attr({ + spellcheck: "false", + tabindex: -1 + }); + } + function prepInput($input, www) { + $input.data(keys.attrs, { + dir: $input.attr("dir"), + autocomplete: $input.attr("autocomplete"), + spellcheck: $input.attr("spellcheck"), + style: $input.attr("style") + }); + $input.addClass(www.classes.input).attr({ + spellcheck: false + }); + try { + !$input.attr("dir") && $input.attr("dir", "auto"); + } catch (e) {} + return $input; + } + function getBackgroundStyles($el) { + return { + backgroundAttachment: $el.css("background-attachment"), + backgroundClip: $el.css("background-clip"), + backgroundColor: $el.css("background-color"), + backgroundImage: $el.css("background-image"), + backgroundOrigin: $el.css("background-origin"), + backgroundPosition: $el.css("background-position"), + backgroundRepeat: $el.css("background-repeat"), + backgroundSize: $el.css("background-size") + }; + } + function revert($input) { + var www, $wrapper; + www = $input.data(keys.www); + $wrapper = $input.parent().filter(www.selectors.wrapper); + _.each($input.data(keys.attrs), function(val, key) { + _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); + }); + $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input); + if ($wrapper.length) { + $input.detach().insertAfter($wrapper); + $wrapper.remove(); + } + } + function $elOrNull(obj) { + var isValid, $el; + isValid = _.isJQuery(obj) || _.isElement(obj); + $el = isValid ? $(obj).first() : []; + return $el.length ? $el : null; + } + })(); +}); \ No newline at end of file diff --git a/docs/1.4.1/AsyncHTTPClient/search.json b/docs/1.4.1/AsyncHTTPClient/search.json new file mode 100644 index 000000000..5cc1f48bf --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/search.json @@ -0,0 +1 @@ +{"Structs/HTTPClientError.html#/s:s23CustomStringConvertibleP11descriptionSSvp":{"name":"description","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV10invalidURLACvpZ":{"name":"invalidURL","abstract":"

URL provided is invalid.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV9emptyHostACvpZ":{"name":"emptyHost","abstract":"

URL does not contain host.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV17missingSocketPathACvpZ":{"name":"missingSocketPath","abstract":"

URL does not contain a socketPath as a host for http(s)+unix shemes.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV15alreadyShutdownACvpZ":{"name":"alreadyShutdown","abstract":"

Client is shutdown and cannot be used for new requests.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV11emptySchemeACvpZ":{"name":"emptyScheme","abstract":"

URL does not contain scheme.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV17unsupportedSchemeyACSSFZ":{"name":"unsupportedScheme(_:)","abstract":"

Provided URL scheme is not supported, supported schemes are: http and https

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV11readTimeoutACvpZ":{"name":"readTimeout","abstract":"

Request timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV22remoteConnectionClosedACvpZ":{"name":"remoteConnectionClosed","abstract":"

Remote connection was closed unexpectedly.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV9cancelledACvpZ":{"name":"cancelled","abstract":"

Request was cancelled.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV32identityCodingIncorrectlyPresentACvpZ":{"name":"identityCodingIncorrectlyPresent","abstract":"

Request contains invalid identity encoding.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV29chunkedSpecifiedMultipleTimesACvpZ":{"name":"chunkedSpecifiedMultipleTimes","abstract":"

Request contains multiple chunks definitions.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20invalidProxyResponseACvpZ":{"name":"invalidProxyResponse","abstract":"

Proxy response was invalid.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20contentLengthMissingACvpZ":{"name":"contentLengthMissing","abstract":"

Request does not contain Content-Length header.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV27proxyAuthenticationRequiredACvpZ":{"name":"proxyAuthenticationRequired","abstract":"

Proxy Authentication Required.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20redirectLimitReachedACvpZ":{"name":"redirectLimitReached","abstract":"

Redirect Limit reached.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV21redirectCycleDetectedACvpZ":{"name":"redirectCycleDetected","abstract":"

Redirect Cycle detected.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV15uncleanShutdownACvpZ":{"name":"uncleanShutdown","abstract":"

Unclean shutdown.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20traceRequestWithBodyACvpZ":{"name":"traceRequestWithBody","abstract":"

A body was sent in a request with method TRACE.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV23invalidHeaderFieldNamesyACSaySSGFZ":{"name":"invalidHeaderFieldNames(_:)","abstract":"

Header field names contain invalid characters.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV24invalidHeaderFieldValuesyACSaySSGFZ":{"name":"invalidHeaderFieldValues(_:)","abstract":"

Header field values contain invalid characters.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV18bodyLengthMismatchACvpZ":{"name":"bodyLengthMismatch","abstract":"

Body length is not equal to Content-Length.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV21writeAfterRequestSentACvpZ":{"name":"writeAfterRequestSent","abstract":"

Body part was written after request was fully sent.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV19incompatibleHeadersACvpZ":{"name":"incompatibleHeaders","abstract":"

Incompatible headers specified, for example Transfer-Encoding and Content-Length.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html":{"name":"HTTPClientError","abstract":"

Possible client errors.

"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP0C0Qa":{"name":"Response","abstract":"

Undocumented

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didSendRequestHead4task_yAA0B0C4TaskCy_0C0QzG_8NIOHTTP1011HTTPRequestH0VtF":{"name":"didSendRequestHead(task:_:)","abstract":"

Called when the request head is sent. Will be called once.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didSendRequestPart4task_yAA0B0C4TaskCy_0C0QzG_7NIOCore6IODataOtF":{"name":"didSendRequestPart(task:_:)","abstract":"

Called when a part of the request body is sent. Could be called zero or more times.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didSendRequest4taskyAA0B0C4TaskCy_0C0QzG_tF":{"name":"didSendRequest(task:)","abstract":"

Called when the request is fully sent. Will be called once.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","abstract":"

Called when response head is received. Will be called once.","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","abstract":"

Called when part of a response body is received. Could be called zero or more times.","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","abstract":"

Called when error was thrown during request execution. Will be called zero or one time only. Request processing will be stopped after that.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","abstract":"

Called when the complete HTTP request is finished. You must return an instance of your Response associated type. Will be called once, except if an error occurred.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html":{"name":"HTTPClientResponseDelegate","abstract":"

HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed."},"Extensions/URL.html#/s:10Foundation3URLV15AsyncHTTPClientE21httpURLWithSocketPath3uriACSgSS_SStcfc":{"name":"init(httpURLWithSocketPath:uri:)","abstract":"

Initializes a newly created HTTP URL connecting to a unix domain socket path. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “http+unix” scheme.

","parent_name":"URL"},"Extensions/URL.html#/s:10Foundation3URLV15AsyncHTTPClientE22httpsURLWithSocketPath3uriACSgSS_SStcfc":{"name":"init(httpsURLWithSocketPath:uri:)","abstract":"

Initializes a newly created HTTPS URL connecting to a unix domain socket path over TLS. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “https+unix” scheme.

","parent_name":"URL"},"Extensions/URL.html":{"name":"URL"},"Extensions.html#/HTTP1ConnectionProvider":{"name":"HTTP1ConnectionProvider"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B15CopyingDelegateC8Responsea":{"name":"Response","abstract":"

Undocumented

","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B15CopyingDelegateC12chunkHandlerAC7NIOCore15EventLoopFutureCyytGAE10ByteBufferVc_tcfc":{"name":"init(chunkHandler:)","abstract":"

Undocumented

","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"HTTPClientCopyingDelegate"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient19ResponseAccumulatorC0C0a":{"name":"Response","abstract":"

Undocumented

","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient19ResponseAccumulatorC7requestAcA0B0C7RequestV_tcfc":{"name":"init(request:)","abstract":"

Undocumented

","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"ResponseAccumulator"},"Classes/HTTPClient/NWTLSError.html#/status":{"name":"status","abstract":"

TLS error status. List of TLS errors can be found in

","parent_name":"NWTLSError"},"Classes/HTTPClient/NWTLSError.html#/init(_:reason:)":{"name":"init(_:reason:)","abstract":"

initialise a NWTLSError

","parent_name":"NWTLSError"},"Classes/HTTPClient/NWTLSError.html#/description":{"name":"description","parent_name":"NWTLSError"},"Classes/HTTPClient/NWPOSIXError.html#/errorCode":{"name":"errorCode","abstract":"

POSIX error code (enum)

","parent_name":"NWPOSIXError"},"Classes/HTTPClient/NWPOSIXError.html#/init(_:reason:)":{"name":"init(_:reason:)","abstract":"

Initialise a NWPOSIXError

","parent_name":"NWPOSIXError"},"Classes/HTTPClient/NWPOSIXError.html#/description":{"name":"description","parent_name":"NWPOSIXError"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC9eventLoop7NIOCore05EventE0_pvp":{"name":"eventLoop","abstract":"

The EventLoop the delegate will be executed on.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC12futureResult7NIOCore15EventLoopFutureCyxGvp":{"name":"futureResult","abstract":"

EventLoopFuture for the response returned by this request.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC4waitxyKF":{"name":"wait()","abstract":"

Waits for execution of this request to complete.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC6cancelyyF":{"name":"cancel()","abstract":"

Cancels the request execution.

","parent_name":"Task"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV5basic8username8passwordAESS_SStFZ":{"name":"basic(username:password:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV5basic11credentialsAESS_tFZ":{"name":"basic(credentials:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV6bearer6tokensAESS_tFZ":{"name":"bearer(tokens:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV11headerValueSSvp":{"name":"headerValue","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6method8NIOHTTP110HTTPMethodOvp":{"name":"method","abstract":"

Request HTTP method, defaults to GET.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url10Foundation3URLVvp":{"name":"url","abstract":"

Remote URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6schemeSSvp":{"name":"scheme","abstract":"

Remote HTTP scheme, resolved from URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4hostSSvp":{"name":"host","abstract":"

Remote host, resolved from URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV7headers8NIOHTTP111HTTPHeadersVvp":{"name":"headers","abstract":"

Request custom HTTP Headers, defaults to no headers.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4bodyAC4BodyVSgvp":{"name":"body","abstract":"

Request body, defaults to no body.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV16tlsConfiguration6NIOSSL16TLSConfigurationVSgvp":{"name":"tlsConfiguration","abstract":"

Request-specific TLS configuration, defaults to no request-specific TLS configuration.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4bodyAESS_8NIOHTTP110HTTPMethodOAJ11HTTPHeadersVAC4BodyVSgtKcfc":{"name":"init(url:method:headers:body:)","abstract":"

Create HTTP request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4body16tlsConfigurationAESS_8NIOHTTP110HTTPMethodOAK11HTTPHeadersVAC4BodyVSg6NIOSSL16TLSConfigurationVSgtKcfc":{"name":"init(url:method:headers:body:tlsConfiguration:)","abstract":"

Create HTTP request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4bodyAE10Foundation3URLV_8NIOHTTP110HTTPMethodOAM11HTTPHeadersVAC4BodyVSgtKcfc":{"name":"init(url:method:headers:body:)","abstract":"

Create an HTTP Request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4body16tlsConfigurationAE10Foundation3URLV_8NIOHTTP110HTTPMethodOAN11HTTPHeadersVAC4BodyVSg6NIOSSL16TLSConfigurationVSgtKcfc":{"name":"init(url:method:headers:body:tlsConfiguration:)","abstract":"

Create an HTTP Request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6useTLSSbvp":{"name":"useTLS","abstract":"

Whether request will be executed using secure socket.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4portSivp":{"name":"port","abstract":"

Resolved port.

","parent_name":"Request"},"Classes/HTTPClient/Body/StreamWriter.html#/s:15AsyncHTTPClient0B0C4BodyV12StreamWriterV7closureAG7NIOCore15EventLoopFutureCyytGAI6IODataOc_tcfc":{"name":"init(closure:)","abstract":"

Create new StreamWriter

","parent_name":"StreamWriter"},"Classes/HTTPClient/Body/StreamWriter.html#/s:15AsyncHTTPClient0B0C4BodyV12StreamWriterV5writey7NIOCore15EventLoopFutureCyytGAI6IODataOF":{"name":"write(_:)","abstract":"

Write data to server.

","parent_name":"StreamWriter"},"Classes/HTTPClient/Body/StreamWriter.html":{"name":"StreamWriter","abstract":"

Chunk provider.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6lengthSiSgvp":{"name":"length","abstract":"

Body size. Request validation will be failed with HTTPClientErrors.contentLengthMissing if nil,","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6streamy7NIOCore15EventLoopFutureCyytGAE12StreamWriterVcvp":{"name":"stream","abstract":"

Body chunk provider.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV10byteBufferyAE7NIOCore04ByteE0VFZ":{"name":"byteBuffer(_:)","abstract":"

Create and stream body using ByteBuffer.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6stream6length_AESiSg_7NIOCore15EventLoopFutureCyytGAE12StreamWriterVctFZ":{"name":"stream(length:_:)","abstract":"

Create and stream body using StreamWriter.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV4datayAE10Foundation4DataVFZ":{"name":"data(_:)","abstract":"

Create and stream body using Data.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6stringyAESSFZ":{"name":"string(_:)","abstract":"

Create and stream body using String.

","parent_name":"Body"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4hostSSvp":{"name":"host","abstract":"

Remote host of the request.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV6status8NIOHTTP118HTTPResponseStatusOvp":{"name":"status","abstract":"

Response HTTP status.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7version8NIOHTTP111HTTPVersionVvp":{"name":"version","abstract":"

Response HTTP version.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7headers8NIOHTTP111HTTPHeadersVvp":{"name":"headers","abstract":"

Reponse HTTP headers.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4body7NIOCore10ByteBufferVSgvp":{"name":"body","abstract":"

Response body.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4host6status7headers4bodyAESS_8NIOHTTP118HTTPResponseStatusOAJ11HTTPHeadersV7NIOCore10ByteBufferVSgtcfc":{"name":"init(host:status:headers:body:)","abstract":"

Create HTTP Response.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4host6status7version7headers4bodyAESS_8NIOHTTP118HTTPResponseStatusOAK11HTTPVersionVAK11HTTPHeadersV7NIOCore10ByteBufferVSgtcfc":{"name":"init(host:status:version:headers:body:)","abstract":"

Create HTTP Response.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7cookiesSayAC6CookieVGvp":{"name":"cookies","abstract":"

List of HTTP cookies returned by the server.

","parent_name":"Response"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4nameSSvp":{"name":"name","abstract":"

The name of the cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV5valueSSvp":{"name":"value","abstract":"

The cookie’s string value.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4pathSSvp":{"name":"path","abstract":"

The cookie’s path.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6domainSSSgvp":{"name":"domain","abstract":"

The domain of the cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV7expires10Foundation4DateVSgvp":{"name":"expires","abstract":"

The cookie’s expiration date.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6maxAgeSiSgvp":{"name":"maxAge","abstract":"

The cookie’s age in seconds.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV8httpOnlySbvp":{"name":"httpOnly","abstract":"

Whether the cookie should only be sent to HTTP servers.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6secureSbvp":{"name":"secure","abstract":"

Whether the cookie should only be sent over secure channels.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6header13defaultDomainAESgSS_SStcfc":{"name":"init(header:defaultDomain:)","abstract":"

Create a Cookie by parsing a Set-Cookie header.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4name5value4path6domain7expires6maxAge8httpOnly6secureAESS_S3SSg10Foundation4DateVSgSiSgS2btcfc":{"name":"init(name:value:path:domain:expires:maxAge:httpOnly:secure:)","abstract":"

Create HTTP cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Decompression.html#/s:15AsyncHTTPClient0B0C13DecompressionO8disabledyA2EmF":{"name":"disabled","abstract":"

Decompression is disabled.

","parent_name":"Decompression"},"Classes/HTTPClient/Decompression.html#/s:15AsyncHTTPClient0B0C13DecompressionO7enabledyAE18NIOHTTPCompression20NIOHTTPDecompressionO0C5LimitV_tcAEmF":{"name":"enabled(limit:)","abstract":"

Decompression is enabled.

","parent_name":"Decompression"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV11indifferentAEvpZ":{"name":"indifferent","abstract":"

Event Loop will be selected by the library.

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV8delegate2onAE7NIOCore0cD0_p_tFZ":{"name":"delegate(on:)","abstract":"

The delegate will be run on the specified EventLoop (and the Channel if possible).

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV18delegateAndChannel2onAE7NIOCore0cD0_p_tFZ":{"name":"delegateAndChannel(on:)","abstract":"

The delegate and the Channel will be run on the specified EventLoop.

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:s23CustomStringConvertibleP11descriptionSSvp":{"name":"description","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopGroupProvider.html#/s:15AsyncHTTPClient0B0C22EventLoopGroupProviderO6sharedyAE7NIOCore0cdE0_pcAEmF":{"name":"shared(_:)","abstract":"

EventLoopGroup will be provided by the user. Owner of this group is responsible for its lifecycle.

","parent_name":"EventLoopGroupProvider"},"Classes/HTTPClient/EventLoopGroupProvider.html#/s:15AsyncHTTPClient0B0C22EventLoopGroupProviderO9createNewyA2EmF":{"name":"createNew","abstract":"

EventLoopGroup will be created by the client. When syncShutdown is called, created EventLoopGroup will be shut down as well.

","parent_name":"EventLoopGroupProvider"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV4hostSSvp":{"name":"host","abstract":"

Specifies Proxy server host.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV4portSivp":{"name":"port","abstract":"

Specifies Proxy server port.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV13authorizationAC13AuthorizationVSgvp":{"name":"authorization","abstract":"

Specifies Proxy server authorization.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV6server4host4portAGSS_SitFZ":{"name":"server(host:port:)","abstract":"

Create a HTTP proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV6server4host4port13authorizationAGSS_SiAC13AuthorizationVSgtFZ":{"name":"server(host:port:authorization:)","abstract":"

Create a HTTP proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV11socksServer4host4portAGSS_SitFZ":{"name":"socksServer(host:port:)","abstract":"

Create a SOCKSv5 proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/ConnectionPool.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14ConnectionPoolV11idleTimeout7NIOCore10TimeAmountVvp":{"name":"idleTimeout","abstract":"

Undocumented

","parent_name":"ConnectionPool"},"Classes/HTTPClient/Configuration/ConnectionPool.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14ConnectionPoolV11idleTimeoutAG7NIOCore10TimeAmountV_tcfc":{"name":"init(idleTimeout:)","abstract":"

Undocumented

","parent_name":"ConnectionPool"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08RedirectC0V8disallowAGvpZ":{"name":"disallow","abstract":"

Redirects are not followed.

","parent_name":"RedirectConfiguration"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08RedirectC0V6follow3max11allowCyclesAGSi_SbtFZ":{"name":"follow(max:allowCycles:)","abstract":"

Redirects are followed with a specified limit.

","parent_name":"RedirectConfiguration"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV7connect7NIOCore10TimeAmountVSgvp":{"name":"connect","abstract":"

Specifies connect timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV4read7NIOCore10TimeAmountVSgvp":{"name":"read","abstract":"

Specifies read timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV7connect4readAG7NIOCore10TimeAmountVSg_AMtcfc":{"name":"init(connect:read:)","abstract":"

Create timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC06NIOSSL16TLSConfigurationVSgvp":{"name":"tlsConfiguration","abstract":"

TLS configuration, defaults to TLSConfiguration.forClient().

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08redirectC0AE08RedirectC0Vvp":{"name":"redirectConfiguration","abstract":"

Enables following 3xx redirects automatically, defaults to RedirectConfiguration().

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7timeoutAE7TimeoutVvp":{"name":"timeout","abstract":"

Default client timeout, defaults to no read timeout and 10 seconds connect timeout.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14connectionPoolAE010ConnectionE0Vvp":{"name":"connectionPool","abstract":"

Connection pool configuration.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5proxyAE5ProxyVSgvp":{"name":"proxy","abstract":"

Upstream proxy, defaults to no proxy.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV13decompressionAC13DecompressionOvp":{"name":"decompression","abstract":"

Enables automatic body decompression. Supported algorithms are gzip and deflate.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV24ignoreUncleanSSLShutdownSbvp":{"name":"ignoreUncleanSSLShutdown","abstract":"

Ignore TLS unclean shutdown error, defaults to false.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC008redirectC07timeout14connectionPool5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL16TLSConfigurationVSg_AE08RedirectC0VSgAE7TimeoutVAE010ConnectionH0VAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(tlsConfiguration:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC008redirectC07timeout5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL16TLSConfigurationVSg_AE08RedirectC0VSgAE7TimeoutVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(tlsConfiguration:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout38maximumAllowedIdleTimeInConnectionPool5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutV7NIOCore0K6AmountVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:maximumAllowedIdleTimeInConnectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout14connectionPool5proxy24ignoreUncleanSSLShutdown13decompression24backgroundActivityLoggerAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutV7NIOCore10TimeAmountVAE5ProxyVSgSbAC13DecompressionO7Logging0Q0VSgtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:backgroundActivityLogger:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/Timeout.html":{"name":"Timeout","abstract":"

Timeout configuration.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html":{"name":"RedirectConfiguration","abstract":"

Specifies redirect processing settings.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/ConnectionPool.html":{"name":"ConnectionPool","abstract":"

Connection pool configuration.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/Proxy.html":{"name":"Proxy","abstract":"

Proxy server configuration","parent_name":"Configuration"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C14eventLoopGroup7NIOCore05EventdE0_pvp":{"name":"eventLoopGroup","abstract":"

Undocumented

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C22eventLoopGroupProvider13configurationA2C05EventdeF0O_AC13ConfigurationVtcfc":{"name":"init(eventLoopGroupProvider:configuration:)","abstract":"

Create an HTTPClient with specified EventLoopGroup provider and configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C22eventLoopGroupProvider13configuration24backgroundActivityLoggerA2C05EventdeF0O_AC13ConfigurationV7Logging0J0Vtcfc":{"name":"init(eventLoopGroupProvider:configuration:backgroundActivityLogger:)","abstract":"

Create an HTTPClient with specified EventLoopGroup provider and configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C12syncShutdownyyKF":{"name":"syncShutdown()","abstract":"

Shuts down the client and EventLoopGroup if it was created by the client.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C8shutdown5queue_y8Dispatch0E5QueueC_ys5Error_pSgctF":{"name":"shutdown(queue:_:)","abstract":"

Shuts down the client and event loop gracefully. This function is clearly an outlier in that it uses a completion","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3get3url8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AG11NIODeadlineVSgtF":{"name":"get(url:deadline:)","abstract":"

Execute GET request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3get3url8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"get(url:deadline:logger:)","abstract":"

Execute GET request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C4post3url4body8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"post(url:body:deadline:)","abstract":"

Execute POST request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C4post3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"post(url:body:deadline:logger:)","abstract":"

Execute POST request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C5patch3url4body8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"patch(url:body:deadline:)","abstract":"

Execute PATCH request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C5patch3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"patch(url:body:deadline:logger:)","abstract":"

Execute PATCH request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3put3url4body8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"put(url:body:deadline:)","abstract":"

Execute PUT request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3put3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"put(url:body:deadline:logger:)","abstract":"

Execute PUT request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C6delete3url8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AG11NIODeadlineVSgtF":{"name":"delete(url:deadline:)","abstract":"

Execute DELETE request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C6delete3url8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"delete(url:deadline:logger:)","abstract":"

Execute DELETE request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_SSAC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:url:body:deadline:logger:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_10socketPath03urlE04body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_S2SAC4BodyVSgAJ11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:socketPath:urlPath:body:deadline:logger:)","abstract":"

Execute arbitrary HTTP+UNIX request to a unix domain socket path, using the specified URL as the request to send to the server.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_16secureSocketPath03urlF04body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_S2SAC4BodyVSgAJ11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:secureSocketPath:urlPath:body:deadline:logger:)","abstract":"

Execute arbitrary HTTPS+UNIX request to a unix domain socket path over TLS, using the specified URL as the request to send to the server.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGAC7RequestV_AG11NIODeadlineVSgtF":{"name":"execute(request:deadline:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGAC7RequestV_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"execute(request:deadline:logger:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request9eventLoop8deadline7NIOCore05EventF6FutureCyAC8ResponseVGAC7RequestV_AC0iF10PreferenceVAH11NIODeadlineVSgtF":{"name":"execute(request:eventLoop:deadline:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request9eventLoop8deadline6logger7NIOCore05EventF6FutureCyAC8ResponseVGAC7RequestV_AC0jF10PreferenceVAI11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(request:eventLoop:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate8deadlineAC4TaskCy_8ResponseQzGAC7RequestV_x7NIOCore11NIODeadlineVSgtAA0bH8DelegateRzlF":{"name":"execute(request:delegate:deadline:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate8deadline6loggerAC4TaskCy_8ResponseQzGAC7RequestV_x7NIOCore11NIODeadlineVSg7Logging6LoggerVtAA0bI8DelegateRzlF":{"name":"execute(request:delegate:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate9eventLoop8deadlineAC4TaskCy_8ResponseQzGAC7RequestV_xAC05EventG10PreferenceV7NIOCore11NIODeadlineVSgtAA0bJ8DelegateRzlF":{"name":"execute(request:delegate:eventLoop:deadline:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate9eventLoop8deadline6loggerAC4TaskCy_8ResponseQzGAC7RequestV_xAC05EventG10PreferenceV7NIOCore11NIODeadlineVSg7Logging6LoggerVSgtAA0bK8DelegateRzlF":{"name":"execute(request:delegate:eventLoop:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Configuration.html":{"name":"Configuration","abstract":"

HTTPClient configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient/EventLoopGroupProvider.html":{"name":"EventLoopGroupProvider","abstract":"

Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

","parent_name":"HTTPClient"},"Classes/HTTPClient/EventLoopPreference.html":{"name":"EventLoopPreference","abstract":"

Specifies how the library will treat event loop passed by the user.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Decompression.html":{"name":"Decompression","abstract":"

Specifies decompression settings.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Cookie.html":{"name":"Cookie","abstract":"

A representation of an HTTP cookie.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Response.html":{"name":"Response","abstract":"

Represent HTTP response.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Body.html":{"name":"Body","abstract":"

Represent request body.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Request.html":{"name":"Request","abstract":"

Represent HTTP request.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Authorization.html":{"name":"Authorization","abstract":"

HTTP authentication

","parent_name":"HTTPClient"},"Classes/HTTPClient/Task.html":{"name":"Task","abstract":"

Response execution context. Will be created by the library and could be used for obtaining","parent_name":"HTTPClient"},"Classes/HTTPClient/NWPOSIXError.html":{"name":"NWPOSIXError","parent_name":"HTTPClient"},"Classes/HTTPClient/NWTLSError.html":{"name":"NWTLSError","parent_name":"HTTPClient"},"Classes/FileDownloadDelegate/Progress.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8ProgressV10totalBytesSiSgvp":{"name":"totalBytes","abstract":"

Undocumented

","parent_name":"Progress"},"Classes/FileDownloadDelegate/Progress.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8ProgressV13receivedBytesSivp":{"name":"receivedBytes","abstract":"

Undocumented

","parent_name":"Progress"},"Classes/FileDownloadDelegate/Progress.html":{"name":"Progress","abstract":"

The response type for this delegate: the total count of bytes as reported by the response","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8Responsea":{"name":"Response","abstract":"

Undocumented

","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient20FileDownloadDelegateC4path4pool10reportHead0H8ProgressACSS_8NIOPosix13NIOThreadPoolCy8NIOHTTP1012HTTPResponseI0VcSgyAC0J0VcSgtKcfc":{"name":"init(path:pool:reportHead:reportProgress:)","abstract":"

Initializes a new file download delegate.

","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html":{"name":"FileDownloadDelegate","abstract":"

Handles a streaming download to a given file path, allowing headers and progress to be reported.

"},"Classes/HTTPClient.html":{"name":"HTTPClient","abstract":"

HTTPClient class provides API for request execution.

"},"Classes/ResponseAccumulator.html":{"name":"ResponseAccumulator","abstract":"

Undocumented

"},"Classes/HTTPClientCopyingDelegate.html":{"name":"HTTPClientCopyingDelegate","abstract":"

Undocumented

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Extensions.html":{"name":"Extensions","abstract":"

The following extensions are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"},"Structs.html":{"name":"Structures","abstract":"

The following structures are available globally.

"}} \ No newline at end of file diff --git a/docs/1.4.1/AsyncHTTPClient/undocumented.json b/docs/1.4.1/AsyncHTTPClient/undocumented.json new file mode 100644 index 000000000..daaebc5e9 --- /dev/null +++ b/docs/1.4.1/AsyncHTTPClient/undocumented.json @@ -0,0 +1,159 @@ +{ + "warnings": [ + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 23, + "symbol": "FileDownloadDelegate.Progress.totalBytes", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 24, + "symbol": "FileDownloadDelegate.Progress.receivedBytes", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 29, + "symbol": "FileDownloadDelegate.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 67, + "symbol": "HTTPClient.eventLoopGroup", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 664, + "symbol": "HTTPClient.Configuration.init(tlsConfiguration:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 680, + "symbol": "HTTPClient.Configuration.init(tlsConfiguration:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 697, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:maximumAllowedIdleTimeInConnectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 713, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:backgroundActivityLogger:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 730, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 878, + "symbol": "HTTPClient.Configuration.ConnectionPool.idleTimeout", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 880, + "symbol": "HTTPClient.Configuration.ConnectionPool.init(idleTimeout:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 357, + "symbol": "HTTPClient.Authorization.basic(username:password:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 361, + "symbol": "HTTPClient.Authorization.basic(credentials:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 365, + "symbol": "HTTPClient.Authorization.bearer(tokens:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 369, + "symbol": "HTTPClient.Authorization.headerValue", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 380, + "symbol": "ResponseAccumulator", + "symbol_kind": "source.lang.swift.decl.class", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 381, + "symbol": "ResponseAccumulator.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 394, + "symbol": "ResponseAccumulator.init(request:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 487, + "symbol": "HTTPClientResponseDelegate.Response", + "symbol_kind": "source.lang.swift.decl.associatedtype", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 38, + "symbol": "HTTPClientCopyingDelegate", + "symbol_kind": "source.lang.swift.decl.class", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 39, + "symbol": "HTTPClientCopyingDelegate.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 43, + "symbol": "HTTPClientCopyingDelegate.init(chunkHandler:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + } + ], + "source_directory": "/code" +} \ No newline at end of file diff --git a/docs/1.5.0/AsyncHTTPClient/Classes.html b/docs/1.5.0/AsyncHTTPClient/Classes.html new file mode 100644 index 000000000..631d2cc50 --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes.html @@ -0,0 +1,315 @@ + + + + Classes Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Classes

+

The following classes are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + FileDownloadDelegate + +
    +
    +
    +
    +
    +
    +

    Handles a streaming download to a given file path, allowing headers and progress to be reported.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class FileDownloadDelegate : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + HTTPClient + +
    +
    +
    +
    +
    +
    +

    HTTPClient class provides API for request execution.

    + +

    Example:

    +
        let client = HTTPClient(eventLoopGroupProvider: .createNew)
    +    client.get(url: "https://swift.org/", deadline: .now() + .seconds(1)).whenComplete { result in
    +        switch result {
    +        case .failure(let error):
    +            // process error
    +        case .success(let response):
    +            if let response.status == .ok {
    +                // handle response
    +            } else {
    +                // handle remote error
    +            }
    +        }
    +    }
    +
    + +

    It is important to close the client instance, for example in a defer statement, after use to cleanly shutdown the underlying NIO EventLoopGroup:

    +
        try client.syncShutdown()
    +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class HTTPClient
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + ResponseAccumulator + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class ResponseAccumulator : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Undocumented

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class HTTPClientCopyingDelegate : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.5.0/AsyncHTTPClient/Classes/FileDownloadDelegate.html b/docs/1.5.0/AsyncHTTPClient/Classes/FileDownloadDelegate.html new file mode 100644 index 000000000..247af83f6 --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes/FileDownloadDelegate.html @@ -0,0 +1,458 @@ + + + + FileDownloadDelegate Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

FileDownloadDelegate

+
+
+ +
public final class FileDownloadDelegate : HTTPClientResponseDelegate
+ +
+
+

Handles a streaming download to a given file path, allowing headers and progress to be reported.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + Progress + +
    +
    +
    +
    +
    +
    +

    The response type for this delegate: the total count of bytes as reported by the response +“Content-Length” header (if available) and the count of bytes downloaded.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Progress
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public typealias Response = Progress
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Initializes a new file download delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(
    +    path: String,
    +    pool: NIOThreadPool = NIOThreadPool(numberOfThreads: 1),
    +    reportHead: ((HTTPResponseHead) -> Void)? = nil,
    +    reportProgress: ((Progress) -> Void)? = nil
    +) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + path + + +
    +

    Path to a file you’d like to write the download to.

    +
    +
    + + pool + + +
    +

    A thread pool to use for asynchronous file I/O.

    +
    +
    + + reportHead + + +
    +

    A closure called when the response head is available.

    +
    +
    + + reportProgress + + +
    +

    A closure called when a body chunk has been downloaded, with +the total byte count and download byte count passed to it as arguments. The callbacks +will be invoked in the same threading context that the delegate itself is invoked, +as controlled by EventLoopPreference.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveHead(
    +    task: HTTPClient.Task<Response>,
    +    _ head: HTTPResponseHead
    +) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveBodyPart(
    +    task: HTTPClient.Task<Response>,
    +    _ buffer: ByteBuffer
    +) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveError(task: HTTPClient.Task<Progress>, _ error: Error)
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didFinishRequest(task: HTTPClient.Task<Response>) throws -> Response
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.5.0/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html b/docs/1.5.0/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html new file mode 100644 index 000000000..ab21df45b --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html @@ -0,0 +1,242 @@ + + + + Progress Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Progress

+
+
+ +
public struct Progress
+ +
+
+

The response type for this delegate: the total count of bytes as reported by the response +“Content-Length” header (if available) and the count of bytes downloaded.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + totalBytes + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var totalBytes: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + receivedBytes + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var receivedBytes: Int
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient.html b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient.html new file mode 100644 index 000000000..2c53a94e1 --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient.html @@ -0,0 +1,2473 @@ + + + + HTTPClient Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClient

+
+
+ +
public class HTTPClient
+ +
+
+

HTTPClient class provides API for request execution.

+ +

Example:

+
    let client = HTTPClient(eventLoopGroupProvider: .createNew)
+    client.get(url: "https://swift.org/", deadline: .now() + .seconds(1)).whenComplete { result in
+        switch result {
+        case .failure(let error):
+            // process error
+        case .success(let response):
+            if let response.status == .ok {
+                // handle response
+            } else {
+                // handle remote error
+            }
+        }
+    }
+
+ +

It is important to close the client instance, for example in a defer statement, after use to cleanly shutdown the underlying NIO EventLoopGroup:

+
    try client.syncShutdown()
+
+ + +
+
+ +
+
+
+
    +
  • +
    + + + + eventLoopGroup + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let eventLoopGroup: EventLoopGroup
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTPClient with specified EventLoopGroup provider and configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public convenience init(eventLoopGroupProvider: EventLoopGroupProvider,
    +                        configuration: Configuration = Configuration())
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + eventLoopGroupProvider + + +
    +

    Specify how EventLoopGroup will be created.

    +
    +
    + + configuration + + +
    +

    Client configuration.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTPClient with specified EventLoopGroup provider and configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public required init(eventLoopGroupProvider: EventLoopGroupProvider,
    +                     configuration: Configuration = Configuration(),
    +                     backgroundActivityLogger: Logger)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + eventLoopGroupProvider + + +
    +

    Specify how EventLoopGroup will be created.

    +
    +
    + + configuration + + +
    +

    Client configuration.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + syncShutdown() + +
    +
    +
    +
    +
    +
    +

    Shuts down the client and EventLoopGroup if it was created by the client.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func syncShutdown() throws
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + shutdown(queue:_:) + +
    +
    +
    +
    +
    +
    +

    Shuts down the client and event loop gracefully. This function is clearly an outlier in that it uses a completion +callback instead of an EventLoopFuture. The reason for that is that NIO’s EventLoopFutures will call back on an event loop. +The virtue of this function is to shut the event loop down. To work around that we call back on a DispatchQueue +instead.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func shutdown(queue: DispatchQueue = .global(), _ callback: @escaping (Error?) -> Void)
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + get(url:deadline:) + +
    +
    +
    +
    +
    +
    +

    Execute GET request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func get(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute GET request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func get(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute POST request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute POST request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PATCH request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PATCH request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PUT request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PUT request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + delete(url:deadline:) + +
    +
    +
    +
    +
    +
    +

    Execute DELETE request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func delete(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    The time when the request must have been completed by.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute DELETE request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func delete(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    The time when the request must have been completed by.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + url + + +
    +

    Request url.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP+UNIX request to a unix domain socket path, using the specified URL as the request to send to the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, socketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + urlPath + + +
    +

    The URL path and query that will be sent to the server.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTPS+UNIX request to a unix domain socket path over TLS, using the specified URL as the request to send to the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, secureSocketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + secureSocketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + urlPath + + +
    +

    The URL path and query that will be sent to the server.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, eventLoop: EventLoopPreference, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request,
    +                    eventLoop eventLoopPreference: EventLoopPreference,
    +                    deadline: NIODeadline? = nil,
    +                    logger: Logger?) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          deadline: NIODeadline? = nil) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          deadline: NIODeadline? = nil,
    +                                                          logger: Logger) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          eventLoop eventLoopPreference: EventLoopPreference,
    +                                                          deadline: NIODeadline? = nil) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          eventLoop eventLoopPreference: EventLoopPreference,
    +                                                          deadline: NIODeadline? = nil,
    +                                                          logger originalLogger: Logger?) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + Configuration + +
    +
    +
    +
    +
    +
    +

    HTTPClient configuration.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Configuration
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum EventLoopGroupProvider
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + EventLoopPreference + +
    +
    +
    +
    +
    +
    +

    Specifies how the library will treat event loop passed by the user.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct EventLoopPreference
    +
    extension HTTPClient.EventLoopPreference: CustomStringConvertible
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Decompression + +
    +
    +
    +
    +
    +
    +

    Specifies decompression settings.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum Decompression
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Cookie + +
    +
    +
    +
    +
    +
    +

    A representation of an HTTP cookie.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Cookie
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Represent HTTP response.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Response
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Body + +
    +
    +
    +
    +
    +
    +

    Represent request body.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Body
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Request + +
    +
    +
    +
    +
    +
    +

    Represent HTTP request.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Request
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Authorization + +
    +
    +
    +
    +
    +
    +

    HTTP authentication

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Authorization : Hashable
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Task + +
    +
    +
    +
    +
    +
    +

    Response execution context. Will be created by the library and could be used for obtaining +EventLoopFuture<Response> of the execution or cancellation of the execution.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class Task<Response>
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + NWPOSIXError + +
    +
    +
    +
    +
    +
    + + See more +
    +
    +
    +
  • +
  • +
    + + + + NWTLSError + +
    +
    +
    +
    +
    +
    + + See more +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Authorization.html b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Authorization.html new file mode 100644 index 000000000..2a5c222ff --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Authorization.html @@ -0,0 +1,301 @@ + + + + Authorization Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Authorization

+
+
+ +
public struct Authorization : Hashable
+ +
+
+

HTTP authentication

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + +
+ diff --git a/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Body.html b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Body.html new file mode 100644 index 000000000..ef800b8fe --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Body.html @@ -0,0 +1,482 @@ + + + + Body Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Body

+
+
+ +
public struct Body
+ +
+
+

Represent request body.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + StreamWriter + +
    +
    +
    +
    +
    +
    +

    Chunk provider.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct StreamWriter
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + length + +
    +
    +
    +
    +
    +
    +

    Body size. Request validation will be failed with HTTPClientErrors.contentLengthMissing if nil, +unless Trasfer-Encoding: chunked header is set.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var length: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + stream + +
    +
    +
    +
    +
    +
    +

    Body chunk provider.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var stream: (StreamWriter) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + byteBuffer(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using ByteBuffer.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func byteBuffer(_ buffer: ByteBuffer) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + buffer + + +
    +

    Body ByteBuffer representation.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + stream(length:_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using StreamWriter.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func stream(length: Int? = nil, _ stream: @escaping (StreamWriter) -> EventLoopFuture<Void>) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + length + + +
    +

    Body size. Request validation will be failed with HTTPClientErrors.contentLengthMissing if nil, + unless Transfer-Encoding: chunked header is set.

    +
    +
    + + stream + + +
    +

    Body chunk provider.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + data(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using Data.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func data(_ data: Data) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    Body Data representation.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + string(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using String.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func string(_ string: String) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + string + + +
    +

    Body String representation.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html new file mode 100644 index 000000000..446a3fcdc --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html @@ -0,0 +1,279 @@ + + + + StreamWriter Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

StreamWriter

+
+
+ +
public struct StreamWriter
+ +
+
+

Chunk provider.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + init(closure:) + +
    +
    +
    +
    +
    +
    +

    Create new StreamWriter

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(closure: @escaping (IOData) -> EventLoopFuture<Void>)
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + closure + + +
    +

    function that will be called to write actual bytes to the channel.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + write(_:) + +
    +
    +
    +
    +
    +
    +

    Write data to server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func write(_ data: IOData) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    IOData to write.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Configuration.html b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Configuration.html new file mode 100644 index 000000000..64f117ba6 --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Configuration.html @@ -0,0 +1,715 @@ + + + + Configuration Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Configuration

+
+
+ +
public struct Configuration
+ +
+
+

HTTPClient configuration.

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + +
+ diff --git a/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html new file mode 100644 index 000000000..47d5a5932 --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html @@ -0,0 +1,241 @@ + + + + ConnectionPool Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

ConnectionPool

+
+
+ +
public struct ConnectionPool : Hashable
+ +
+
+

Connection pool configuration.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + idleTimeout + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var idleTimeout: TimeAmount
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + init(idleTimeout:) + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(idleTimeout: TimeAmount = .seconds(60))
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html new file mode 100644 index 000000000..412d31613 --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html @@ -0,0 +1,479 @@ + + + + Proxy Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Proxy

+
+
+ +
public struct Proxy
+ +
+
+

Proxy server configuration +Specifies the remote address of an HTTP proxy.

+ +

Adding an Proxy to your client’s HTTPClient.Configuration +will cause requests to be passed through the specified proxy using the +HTTP CONNECT method.

+ +

If a TLSConfiguration is used in conjunction with HTTPClient.Configuration.Proxy, +TLS will be established after successful proxy, between your client +and the destination server.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server host.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + port + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server port.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var port: Int
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + authorization + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server authorization.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var authorization: HTTPClient.Authorization? { get set }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + server(host:port:) + +
    +
    +
    +
    +
    +
    +

    Create a HTTP proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func server(host: String, port: Int) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + host + + +
    +

    proxy server host.

    +
    +
    + + port + + +
    +

    proxy server port.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a HTTP proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func server(host: String, port: Int, authorization: HTTPClient.Authorization? = nil) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + host + + +
    +

    proxy server host.

    +
    +
    + + port + + +
    +

    proxy server port.

    +
    +
    + + authorization + + +
    +

    proxy server authorization.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a SOCKSv5 proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func socksServer(host: String, port: Int = 1080) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + host + + +
    +

    The SOCKSv5 proxy address.

    +
    +
    + + port + + +
    +

    The SOCKSv5 proxy port, defaults to 1080.

    +
    +
    +
    +
    +

    Return Value

    +

    A new instance of Proxy configured to connect to a SOCKSv5 server.

    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html new file mode 100644 index 000000000..f8486cde0 --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html @@ -0,0 +1,277 @@ + + + + RedirectConfiguration Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

RedirectConfiguration

+
+
+ +
public struct RedirectConfiguration
+ +
+
+

Specifies redirect processing settings.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + disallow + +
    +
    +
    +
    +
    +
    +

    Redirects are not followed.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let disallow: HTTPClient.Configuration.RedirectConfiguration
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Redirects are followed with a specified limit.

    +
    +

    Warning

    +

    Cycle detection will keep all visited URLs in memory which means a malicious server could use this as a denial-of-service vector.

    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func follow(max: Int, allowCycles: Bool) -> RedirectConfiguration
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + max + + +
    +

    The maximum number of allowed redirects.

    +
    +
    + + allowCycles + + +
    +

    Whether cycles are allowed.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html new file mode 100644 index 000000000..25528cb25 --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html @@ -0,0 +1,303 @@ + + + + Timeout Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Timeout

+
+
+ +
public struct Timeout
+ +
+
+

Timeout configuration.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + connect + +
    +
    +
    +
    +
    +
    +

    Specifies connect timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var connect: TimeAmount?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + read + +
    +
    +
    +
    +
    +
    +

    Specifies read timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var read: TimeAmount?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + init(connect:read:) + +
    +
    +
    +
    +
    +
    +

    Create timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(connect: TimeAmount? = nil, read: TimeAmount? = nil)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + connect + + +
    +

    connect timeout. Will default to 10 seconds, if no value is +provided. See var connectionCreationTimeout

    +
    +
    + + read + + +
    +

    read timeout.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Cookie.html b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Cookie.html new file mode 100644 index 000000000..eb32e55b3 --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Cookie.html @@ -0,0 +1,619 @@ + + + + Cookie Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Cookie

+
+
+ +
public struct Cookie
+ +
+
+

A representation of an HTTP cookie.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + name + +
    +
    +
    +
    +
    +
    +

    The name of the cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var name: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + value + +
    +
    +
    +
    +
    +
    +

    The cookie’s string value.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var value: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + path + +
    +
    +
    +
    +
    +
    +

    The cookie’s path.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var path: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + domain + +
    +
    +
    +
    +
    +
    +

    The domain of the cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var domain: String?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + expires + +
    +
    +
    +
    +
    +
    +

    The cookie’s expiration date.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var expires: Date?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + maxAge + +
    +
    +
    +
    +
    +
    +

    The cookie’s age in seconds.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var maxAge: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + httpOnly + +
    +
    +
    +
    +
    +
    +

    Whether the cookie should only be sent to HTTP servers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var httpOnly: Bool
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + secure + +
    +
    +
    +
    +
    +
    +

    Whether the cookie should only be sent over secure channels.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var secure: Bool
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a Cookie by parsing a Set-Cookie header.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(header: String, defaultDomain: String)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + header + + +
    +

    String representation of the Set-Cookie response header.

    +
    +
    + + defaultDomain + + +
    +

    Default domain to use if cookie was sent without one.

    +
    +
    +
    +
    +

    Return Value

    +

    nil if the header is invalid.

    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(name: String, value: String, path: String = "/", domain: String? = nil, expires: Date? = nil, maxAge: Int? = nil, httpOnly: Bool = false, secure: Bool = false)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + name + + +
    +

    The name of the cookie.

    +
    +
    + + value + + +
    +

    The cookie’s string value.

    +
    +
    + + path + + +
    +

    The cookie’s path.

    +
    +
    + + domain + + +
    +

    The domain of the cookie, defaults to nil.

    +
    +
    + + expires + + +
    +

    The cookie’s expiration date, defaults to nil.

    +
    +
    + + maxAge + + +
    +

    The cookie’s age in seconds, defaults to nil.

    +
    +
    + + httpOnly + + +
    +

    Whether this cookie should be used by HTTP servers only, defaults to false.

    +
    +
    + + secure + + +
    +

    Whether this cookie should only be sent using secure channels, defaults to false.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Decompression.html b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Decompression.html new file mode 100644 index 000000000..3dbcc2081 --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Decompression.html @@ -0,0 +1,241 @@ + + + + Decompression Enumeration Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Decompression

+
+
+ +
public enum Decompression
+ +
+
+

Specifies decompression settings.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + disabled + +
    +
    +
    +
    +
    +
    +

    Decompression is disabled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case disabled
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + enabled(limit:) + +
    +
    +
    +
    +
    +
    +

    Decompression is enabled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case enabled(limit: NIOHTTPDecompression.DecompressionLimit)
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html new file mode 100644 index 000000000..c726d751e --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html @@ -0,0 +1,241 @@ + + + + EventLoopGroupProvider Enumeration Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

EventLoopGroupProvider

+
+
+ +
public enum EventLoopGroupProvider
+ +
+
+

Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + shared(_:) + +
    +
    +
    +
    +
    +
    +

    EventLoopGroup will be provided by the user. Owner of this group is responsible for its lifecycle.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case shared(EventLoopGroup)
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + createNew + +
    +
    +
    +
    +
    +
    +

    EventLoopGroup will be created by the client. When syncShutdown is called, created EventLoopGroup will be shut down as well.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case createNew
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html new file mode 100644 index 000000000..635ff8d68 --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html @@ -0,0 +1,308 @@ + + + + EventLoopPreference Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

EventLoopPreference

+
+
+ +
public struct EventLoopPreference
+
extension HTTPClient.EventLoopPreference: CustomStringConvertible
+ +
+
+

Specifies how the library will treat event loop passed by the user.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + indifferent + +
    +
    +
    +
    +
    +
    +

    Event Loop will be selected by the library.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let indifferent: HTTPClient.EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + delegate(on:) + +
    +
    +
    +
    +
    +
    +

    The delegate will be run on the specified EventLoop (and the Channel if possible).

    + +

    This will call the configured delegate on eventLoop and will try to use a Channel on the same +EventLoop but will not establish a new network connection just to satisfy the EventLoop preference if +another existing connection on a different EventLoop is readily available from a connection pool.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func delegate(on eventLoop: EventLoop) -> EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The delegate and the Channel will be run on the specified EventLoop.

    + +

    Use this for use-cases where you prefer a new connection to be established over re-using an existing +connection that might be on a different EventLoop.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func delegateAndChannel(on eventLoop: EventLoop) -> EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var description: String { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/NWPOSIXError.html b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/NWPOSIXError.html new file mode 100644 index 000000000..9dd46738c --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/NWPOSIXError.html @@ -0,0 +1,226 @@ + + + + NWPOSIXError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

NWPOSIXError

+ +
+
+ +
+
+
+
    +
  • +
    + + + + errorCode + +
    +
    +
    +
    +
    +
    +

    POSIX error code (enum)

    + +
    +
    +
    +
  • +
  • +
    + + + + init(_:reason:) + +
    +
    +
    +
    +
    +
    +

    Initialise a NWPOSIXError

    + +
    +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/NWTLSError.html b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/NWTLSError.html new file mode 100644 index 000000000..907ad5794 --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/NWTLSError.html @@ -0,0 +1,226 @@ + + + + NWTLSError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

NWTLSError

+ +
+
+ +
+
+
+
    +
  • +
    + + + + status + +
    +
    +
    +
    +
    +
    +

    TLS error status. List of TLS errors can be found in

    + +
    +
    +
    +
  • +
  • +
    + + + + init(_:reason:) + +
    +
    +
    +
    +
    +
    +

    initialise a NWTLSError

    + +
    +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Request.html b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Request.html new file mode 100644 index 000000000..8ba77b8ca --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Request.html @@ -0,0 +1,879 @@ + + + + Request Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Request

+
+
+ +
public struct Request
+ +
+
+

Represent HTTP request.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + method + +
    +
    +
    +
    +
    +
    +

    Request HTTP method, defaults to GET.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let method: HTTPMethod
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + url + +
    +
    +
    +
    +
    +
    +

    Remote URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let url: URL
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + scheme + +
    +
    +
    +
    +
    +
    +

    Remote HTTP scheme, resolved from URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let scheme: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Remote host, resolved from URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + headers + +
    +
    +
    +
    +
    +
    +

    Request custom HTTP Headers, defaults to no headers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var headers: HTTPHeaders
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + body + +
    +
    +
    +
    +
    +
    +

    Request body, defaults to no body.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var body: Body?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + tlsConfiguration + +
    +
    +
    +
    +
    +
    +

    Request-specific TLS configuration, defaults to no request-specific TLS configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var tlsConfiguration: TLSConfiguration?
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP request.

    +
    +

    Throws

    +
      +
    • invalidURL if URL cannot be parsed.
    • +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: String, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + version + + +
    +

    HTTP version.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP request.

    +
    +

    Throws

    +
      +
    • invalidURL if URL cannot be parsed.
    • +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: String, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil, tlsConfiguration: TLSConfiguration?) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + version + + +
    +

    HTTP version.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + tlsConfiguration + + +
    +

    Request TLS configuration

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTP Request.

    +
    +

    Throws

    +
      +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    • missingSocketPath if URL does not contains a socketPath as an encoded host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTP Request.

    +
    +

    Throws

    +
      +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    • missingSocketPath if URL does not contains a socketPath as an encoded host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil, tlsConfiguration: TLSConfiguration?) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + tlsConfiguration + + +
    +

    Request TLS configuration

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + useTLS + +
    +
    +
    +
    +
    +
    +

    Whether request will be executed using secure socket.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var useTLS: Bool { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + port + +
    +
    +
    +
    +
    +
    +

    Resolved port.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var port: Int { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Response.html b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Response.html new file mode 100644 index 000000000..272ba39e4 --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Response.html @@ -0,0 +1,544 @@ + + + + Response Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Response

+
+
+ +
public struct Response
+ +
+
+

Represent HTTP response.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Remote host of the request.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + status + +
    +
    +
    +
    +
    +
    +

    Response HTTP status.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var status: HTTPResponseStatus
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + version + +
    +
    +
    +
    +
    +
    +

    Response HTTP version.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var version: HTTPVersion
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + headers + +
    +
    +
    +
    +
    +
    +

    Reponse HTTP headers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var headers: HTTPHeaders
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + body + +
    +
    +
    +
    +
    +
    +

    Response body.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var body: ByteBuffer?
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP Response.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    @available(*, deprecated, renamed: "init(host:status:version:headers:body:﹚")
    +public init(host: String, status: HTTPResponseStatus, headers: HTTPHeaders, body: ByteBuffer?)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + host + + +
    +

    Remote host of the request.

    +
    +
    + + status + + +
    +

    Response HTTP status.

    +
    +
    + + headers + + +
    +

    Reponse HTTP headers.

    +
    +
    + + body + + +
    +

    Response body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP Response.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(host: String, status: HTTPResponseStatus, version: HTTPVersion, headers: HTTPHeaders, body: ByteBuffer?)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + host + + +
    +

    Remote host of the request.

    +
    +
    + + status + + +
    +

    Response HTTP status.

    +
    +
    + + version + + +
    +

    Response HTTP version.

    +
    +
    + + headers + + +
    +

    Reponse HTTP headers.

    +
    +
    + + body + + +
    +

    Response body.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + cookies + +
    +
    +
    +
    +
    +
    +

    List of HTTP cookies returned by the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var cookies: [HTTPClient.Cookie] { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Task.html b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Task.html new file mode 100644 index 000000000..6db23148f --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClient/Task.html @@ -0,0 +1,311 @@ + + + + Task Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Task

+
+
+ +
public final class Task<Response>
+ +
+
+

Response execution context. Will be created by the library and could be used for obtaining +EventLoopFuture<Response> of the execution or cancellation of the execution.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + eventLoop + +
    +
    +
    +
    +
    +
    +

    The EventLoop the delegate will be executed on.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let eventLoop: EventLoop
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + futureResult + +
    +
    +
    +
    +
    +
    +

    EventLoopFuture for the response returned by this request.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var futureResult: EventLoopFuture<Response> { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + wait() + +
    +
    +
    +
    +
    +
    +

    Waits for execution of this request to complete.

    +
    +

    Throws

    + The error value of the EventLoopFuture if it errors. + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func wait() throws -> Response
    + +
    +
    +
    +

    Return Value

    +

    The value of the EventLoopFuture when it completes.

    +
    + +
    +
    +
  • +
  • +
    + + + + cancel() + +
    +
    +
    +
    +
    +
    +

    Cancels the request execution.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func cancel()
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html new file mode 100644 index 000000000..d2c19811f --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html @@ -0,0 +1,299 @@ + + + + HTTPClientCopyingDelegate Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientCopyingDelegate

+
+
+ +
public final class HTTPClientCopyingDelegate : HTTPClientResponseDelegate
+ +
+
+

Undocumented

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + + diff --git a/docs/1.5.0/AsyncHTTPClient/Classes/ResponseAccumulator.html b/docs/1.5.0/AsyncHTTPClient/Classes/ResponseAccumulator.html new file mode 100644 index 000000000..26e9f69eb --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Classes/ResponseAccumulator.html @@ -0,0 +1,357 @@ + + + + ResponseAccumulator Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

ResponseAccumulator

+
+
+ +
public class ResponseAccumulator : HTTPClientResponseDelegate
+ +
+
+

Undocumented

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + + diff --git a/docs/1.5.0/AsyncHTTPClient/Extensions.html b/docs/1.5.0/AsyncHTTPClient/Extensions.html new file mode 100644 index 000000000..306bd62eb --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Extensions.html @@ -0,0 +1,216 @@ + + + + Extensions Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Extensions

+

The following extensions are available globally.

+ +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + + diff --git a/docs/1.5.0/AsyncHTTPClient/Extensions/URL.html b/docs/1.5.0/AsyncHTTPClient/Extensions/URL.html new file mode 100644 index 000000000..d2e2e3d7a --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Extensions/URL.html @@ -0,0 +1,299 @@ + + + + URL Extension Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

URL

+
+
+ +
extension URL
+ +
+
+ +
+
+ +
+
+
+
    +
  • + +
    +
    +
    +
    +
    +

    Initializes a newly created HTTP URL connecting to a unix domain socket path. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “http+unix” scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(httpURLWithSocketPath socketPath: String, uri: String = "/")
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + uri + + +
    +

    The URI path and query that will be sent to the server.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Initializes a newly created HTTPS URL connecting to a unix domain socket path over TLS. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “https+unix” scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(httpsURLWithSocketPath socketPath: String, uri: String = "/")
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + uri + + +
    +

    The URI path and query that will be sent to the server.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.0/AsyncHTTPClient/Protocols.html b/docs/1.5.0/AsyncHTTPClient/Protocols.html new file mode 100644 index 000000000..47042a86e --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Protocols.html @@ -0,0 +1,234 @@ + + + + Protocols Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Protocols

+

The following protocols are available globally.

+ +
+
+ +
+
+
+
    +
  • + +
    +
    +
    +
    +
    +

    HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed. +You can implement this protocol if you need fine-grained control over an HTTP request/response, for example, if you want to inspect the response +headers before deciding whether to accept a response body, or if you want to stream your request body. Pass an instance of your conforming +class to the HTTPClient.execute() method and this package will call each delegate method appropriately as the request takes place./

    +

    Backpressure

    + +

    A HTTPClientResponseDelegate can be used to exert backpressure on the server response. This is achieved by way of the futures returned from +didReceiveHead and didReceiveBodyPart. The following functions are part of the “backpressure system” in the delegate:

    + +
      +
    • didReceiveHead
    • +
    • didReceiveBodyPart
    • +
    • didFinishRequest
    • +
    • didReceiveError
    • +
    + +

    The first three methods are strictly exclusive, with that exclusivity managed by the futures returned by didReceiveHead and +didReceiveBodyPart. What this means is that until the returned future is completed, none of these three methods will be called +again. This allows delegates to rate limit the server to a capacity it can manage. didFinishRequest does not return a future, +as we are expecting no more data from the server at this time.

    + +

    didReceiveError is somewhat special: it signals the end of this regime. didRecieveError is not exclusive: it may be called at +any time, even if a returned future is not yet completed. didReceiveError is terminal, meaning that once it has been called none +of these four methods will be called again. This can be used as a signal to abandon all outstanding work.

    +
    +

    Note

    + This delegate is strongly held by the HTTPTaskHandler + for the duration of the Request processing and will be + released together with the HTTPTaskHandler when channel is closed. + Users of the library are not required to keep a reference to the + object that implements this protocol, but may do so if needed. + +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public protocol HTTPClientResponseDelegate : AnyObject
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.0/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html b/docs/1.5.0/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html new file mode 100644 index 000000000..6e08b4384 --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html @@ -0,0 +1,716 @@ + + + + HTTPClientResponseDelegate Protocol Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientResponseDelegate

+
+
+ +
public protocol HTTPClientResponseDelegate : AnyObject
+ +
+
+

HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed. +You can implement this protocol if you need fine-grained control over an HTTP request/response, for example, if you want to inspect the response +headers before deciding whether to accept a response body, or if you want to stream your request body. Pass an instance of your conforming +class to the HTTPClient.execute() method and this package will call each delegate method appropriately as the request takes place./

+

Backpressure

+ +

A HTTPClientResponseDelegate can be used to exert backpressure on the server response. This is achieved by way of the futures returned from +didReceiveHead and didReceiveBodyPart. The following functions are part of the “backpressure system” in the delegate:

+ +
    +
  • didReceiveHead
  • +
  • didReceiveBodyPart
  • +
  • didFinishRequest
  • +
  • didReceiveError
  • +
+ +

The first three methods are strictly exclusive, with that exclusivity managed by the futures returned by didReceiveHead and +didReceiveBodyPart. What this means is that until the returned future is completed, none of these three methods will be called +again. This allows delegates to rate limit the server to a capacity it can manage. didFinishRequest does not return a future, +as we are expecting no more data from the server at this time.

+ +

didReceiveError is somewhat special: it signals the end of this regime. didRecieveError is not exclusive: it may be called at +any time, even if a returned future is not yet completed. didReceiveError is terminal, meaning that once it has been called none +of these four methods will be called again. This can be used as a signal to abandon all outstanding work.

+
+

Note

+ This delegate is strongly held by the HTTPTaskHandler + for the duration of the Request processing and will be + released together with the HTTPTaskHandler when channel is closed. + Users of the library are not required to keep a reference to the + object that implements this protocol, but may do so if needed. + +
+ + +
+
+ +
+
+
+
    +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    associatedtype Response
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequestHead(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when the request head is sent. Will be called once.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequestHead(task: HTTPClient.Task<Response>, _ head: HTTPRequestHead)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + head + + +
    +

    Request head.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequestPart(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when a part of the request body is sent. Could be called zero or more times.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequestPart(task: HTTPClient.Task<Response>, _ part: IOData)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + part + + +
    +

    Request body Part.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequest(task:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when the request is fully sent. Will be called once.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequest(task: HTTPClient.Task<Response>)
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveHead(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when response head is received. Will be called once. +You must return an EventLoopFuture<Void> that you complete when you have finished processing the body part. +You can create an already succeeded future by calling task.eventLoop.makeSucceededFuture(()).

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveHead(task: HTTPClient.Task<Response>, _ head: HTTPResponseHead) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + head + + +
    +

    Received reposonse head.

    +
    +
    +
    +
    +

    Return Value

    +

    EventLoopFuture that will be used for backpressure.

    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveBodyPart(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when part of a response body is received. Could be called zero or more times. +You must return an EventLoopFuture<Void> that you complete when you have finished processing the body part. +You can create an already succeeded future by calling task.eventLoop.makeSucceededFuture(()).

    + +

    This function will not be called until the future returned by didReceiveHead has completed.

    + +

    This function will not be called for subsequent body parts until the previous future returned by a +call to this function completes.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveBodyPart(task: HTTPClient.Task<Response>, _ buffer: ByteBuffer) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + buffer + + +
    +

    Received body Part.

    +
    +
    +
    +
    +

    Return Value

    +

    EventLoopFuture that will be used for backpressure.

    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveError(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when error was thrown during request execution. Will be called zero or one time only. Request processing will be stopped after that.

    + +

    This function may be called at any time: it does not respect the backpressure exerted by didReceiveHead and didReceiveBodyPart. +All outstanding work may be cancelled when this is received. Once called, no further calls will be made to didReceiveHead, didReceiveBodyPart, +or didFinishRequest.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveError(task: HTTPClient.Task<Response>, _ error: Error)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + error + + +
    +

    Error that occured during response processing.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Called when the complete HTTP request is finished. You must return an instance of your Response associated type. Will be called once, except if an error occurred.

    + +

    This function will not be called until all futures returned by didReceiveHead and didReceiveBodyPart have completed. Once called, +no further calls will be made to didReceiveHead, didReceiveBodyPart, or didReceiveError.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didFinishRequest(task: HTTPClient.Task<Response>) throws -> Response
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    +
    +
    +

    Return Value

    +

    Result of processing.

    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.0/AsyncHTTPClient/Structs.html b/docs/1.5.0/AsyncHTTPClient/Structs.html new file mode 100644 index 000000000..8ca52df7c --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Structs.html @@ -0,0 +1,202 @@ + + + + Structures Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Structures

+

The following structures are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + HTTPClientError + +
    +
    +
    +
    +
    +
    +

    Possible client errors.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct HTTPClientError : Error, Equatable, CustomStringConvertible
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.0/AsyncHTTPClient/Structs/HTTPClientError.html b/docs/1.5.0/AsyncHTTPClient/Structs/HTTPClientError.html new file mode 100644 index 000000000..f02d70d49 --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/Structs/HTTPClientError.html @@ -0,0 +1,1020 @@ + + + + HTTPClientError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientError

+
+
+ +
public struct HTTPClientError : Error, Equatable, CustomStringConvertible
+ +
+
+

Possible client errors.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var description: String { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + invalidURL + +
    +
    +
    +
    +
    +
    +

    URL provided is invalid.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let invalidURL: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + emptyHost + +
    +
    +
    +
    +
    +
    +

    URL does not contain host.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let emptyHost: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + missingSocketPath + +
    +
    +
    +
    +
    +
    +

    URL does not contain a socketPath as a host for http(s)+unix shemes.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let missingSocketPath: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + alreadyShutdown + +
    +
    +
    +
    +
    +
    +

    Client is shutdown and cannot be used for new requests.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let alreadyShutdown: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + emptyScheme + +
    +
    +
    +
    +
    +
    +

    URL does not contain scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let emptyScheme: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + unsupportedScheme(_:) + +
    +
    +
    +
    +
    +
    +

    Provided URL scheme is not supported, supported schemes are: http and https

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func unsupportedScheme(_ scheme: String) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + readTimeout + +
    +
    +
    +
    +
    +
    +

    Request timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let readTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Remote connection was closed unexpectedly.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let remoteConnectionClosed: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + cancelled + +
    +
    +
    +
    +
    +
    +

    Request was cancelled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let cancelled: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Request contains invalid identity encoding.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let identityCodingIncorrectlyPresent: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Request contains multiple chunks definitions.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let chunkedSpecifiedMultipleTimes: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + invalidProxyResponse + +
    +
    +
    +
    +
    +
    +

    Proxy response was invalid.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let invalidProxyResponse: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + contentLengthMissing + +
    +
    +
    +
    +
    +
    +

    Request does not contain Content-Length header.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let contentLengthMissing: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Proxy Authentication Required.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let proxyAuthenticationRequired: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + redirectLimitReached + +
    +
    +
    +
    +
    +
    +

    Redirect Limit reached.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let redirectLimitReached: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + redirectCycleDetected + +
    +
    +
    +
    +
    +
    +

    Redirect Cycle detected.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let redirectCycleDetected: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + uncleanShutdown + +
    +
    +
    +
    +
    +
    +

    Unclean shutdown.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let uncleanShutdown: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + traceRequestWithBody + +
    +
    +
    +
    +
    +
    +

    A body was sent in a request with method TRACE.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let traceRequestWithBody: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Header field names contain invalid characters.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func invalidHeaderFieldNames(_ names: [String]) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + bodyLengthMismatch + +
    +
    +
    +
    +
    +
    +

    Body length is not equal to Content-Length.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let bodyLengthMismatch: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + writeAfterRequestSent + +
    +
    +
    +
    +
    +
    +

    Body part was written after request was fully sent.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let writeAfterRequestSent: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + incompatibleHeaders + +
    +
    +
    +
    +
    +
    +

    Incompatible headers specified, for example Transfer-Encoding and Content-Length.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let incompatibleHeaders: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + connectTimeout + +
    +
    +
    +
    +
    +
    +

    Creating a new tcp connection timed out

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let connectTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + socksHandshakeTimeout + +
    +
    +
    +
    +
    +
    +

    The socks handshake timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let socksHandshakeTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The http proxy connection creation timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let httpProxyHandshakeTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + tlsHandshakeTimeout + +
    +
    +
    +
    +
    +
    +

    The tls handshake timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let tlsHandshakeTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The remote server only offered an unsupported application protocol

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func serverOfferedUnsupportedApplicationProtocol(_ proto: String) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.0/AsyncHTTPClient/badge.svg b/docs/1.5.0/AsyncHTTPClient/badge.svg new file mode 100644 index 000000000..b28dab66c --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/badge.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + documentation + + + documentation + + + 89% + + + 89% + + + diff --git a/docs/1.5.0/AsyncHTTPClient/css/highlight.css b/docs/1.5.0/AsyncHTTPClient/css/highlight.css new file mode 100644 index 000000000..d0db0e13b --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/css/highlight.css @@ -0,0 +1,200 @@ +/* Credit to https://gist.github.com/wataru420/2048287 */ +.highlight { + /* Comment */ + /* Error */ + /* Keyword */ + /* Operator */ + /* Comment.Multiline */ + /* Comment.Preproc */ + /* Comment.Single */ + /* Comment.Special */ + /* Generic.Deleted */ + /* Generic.Deleted.Specific */ + /* Generic.Emph */ + /* Generic.Error */ + /* Generic.Heading */ + /* Generic.Inserted */ + /* Generic.Inserted.Specific */ + /* Generic.Output */ + /* Generic.Prompt */ + /* Generic.Strong */ + /* Generic.Subheading */ + /* Generic.Traceback */ + /* Keyword.Constant */ + /* Keyword.Declaration */ + /* Keyword.Pseudo */ + /* Keyword.Reserved */ + /* Keyword.Type */ + /* Literal.Number */ + /* Literal.String */ + /* Name.Attribute */ + /* Name.Builtin */ + /* Name.Class */ + /* Name.Constant */ + /* Name.Entity */ + /* Name.Exception */ + /* Name.Function */ + /* Name.Namespace */ + /* Name.Tag */ + /* Name.Variable */ + /* Operator.Word */ + /* Text.Whitespace */ + /* Literal.Number.Float */ + /* Literal.Number.Hex */ + /* Literal.Number.Integer */ + /* Literal.Number.Oct */ + /* Literal.String.Backtick */ + /* Literal.String.Char */ + /* Literal.String.Doc */ + /* Literal.String.Double */ + /* Literal.String.Escape */ + /* Literal.String.Heredoc */ + /* Literal.String.Interpol */ + /* Literal.String.Other */ + /* Literal.String.Regex */ + /* Literal.String.Single */ + /* Literal.String.Symbol */ + /* Name.Builtin.Pseudo */ + /* Name.Variable.Class */ + /* Name.Variable.Global */ + /* Name.Variable.Instance */ + /* Literal.Number.Integer.Long */ } + .highlight .c { + color: #999988; + font-style: italic; } + .highlight .err { + color: #a61717; + background-color: #e3d2d2; } + .highlight .k { + color: #000000; + font-weight: bold; } + .highlight .o { + color: #000000; + font-weight: bold; } + .highlight .cm { + color: #999988; + font-style: italic; } + .highlight .cp { + color: #999999; + font-weight: bold; } + .highlight .c1 { + color: #999988; + font-style: italic; } + .highlight .cs { + color: #999999; + font-weight: bold; + font-style: italic; } + .highlight .gd { + color: #000000; + background-color: #ffdddd; } + .highlight .gd .x { + color: #000000; + background-color: #ffaaaa; } + .highlight .ge { + color: #000000; + font-style: italic; } + .highlight .gr { + color: #aa0000; } + .highlight .gh { + color: #999999; } + .highlight .gi { + color: #000000; + background-color: #ddffdd; } + .highlight .gi .x { + color: #000000; + background-color: #aaffaa; } + .highlight .go { + color: #888888; } + .highlight .gp { + color: #555555; } + .highlight .gs { + font-weight: bold; } + .highlight .gu { + color: #aaaaaa; } + .highlight .gt { + color: #aa0000; } + .highlight .kc { + color: #000000; + font-weight: bold; } + .highlight .kd { + color: #000000; + font-weight: bold; } + .highlight .kp { + color: #000000; + font-weight: bold; } + .highlight .kr { + color: #000000; + font-weight: bold; } + .highlight .kt { + color: #445588; } + .highlight .m { + color: #009999; } + .highlight .s { + color: #d14; } + .highlight .na { + color: #008080; } + .highlight .nb { + color: #0086B3; } + .highlight .nc { + color: #445588; + font-weight: bold; } + .highlight .no { + color: #008080; } + .highlight .ni { + color: #800080; } + .highlight .ne { + color: #990000; + font-weight: bold; } + .highlight .nf { + color: #990000; } + .highlight .nn { + color: #555555; } + .highlight .nt { + color: #000080; } + .highlight .nv { + color: #008080; } + .highlight .ow { + color: #000000; + font-weight: bold; } + .highlight .w { + color: #bbbbbb; } + .highlight .mf { + color: #009999; } + .highlight .mh { + color: #009999; } + .highlight .mi { + color: #009999; } + .highlight .mo { + color: #009999; } + .highlight .sb { + color: #d14; } + .highlight .sc { + color: #d14; } + .highlight .sd { + color: #d14; } + .highlight .s2 { + color: #d14; } + .highlight .se { + color: #d14; } + .highlight .sh { + color: #d14; } + .highlight .si { + color: #d14; } + .highlight .sx { + color: #d14; } + .highlight .sr { + color: #009926; } + .highlight .s1 { + color: #d14; } + .highlight .ss { + color: #990073; } + .highlight .bp { + color: #999999; } + .highlight .vc { + color: #008080; } + .highlight .vg { + color: #008080; } + .highlight .vi { + color: #008080; } + .highlight .il { + color: #009999; } diff --git a/docs/1.5.0/AsyncHTTPClient/css/jazzy.css b/docs/1.5.0/AsyncHTTPClient/css/jazzy.css new file mode 100644 index 000000000..7e2eac38f --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/css/jazzy.css @@ -0,0 +1,400 @@ +*, *:before, *:after { + box-sizing: inherit; } + +body { + margin: 0; + background: #fff; + color: #333; + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + letter-spacing: .2px; + -webkit-font-smoothing: antialiased; + box-sizing: border-box; } + +h1 { + font-size: 2rem; + font-weight: 700; + margin: 1.275em 0 0.6em; } + +h2 { + font-size: 1.75rem; + font-weight: 700; + margin: 1.275em 0 0.3em; } + +h3 { + font-size: 1.5rem; + font-weight: 700; + margin: 1em 0 0.3em; } + +h4 { + font-size: 1.25rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h5 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h6 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; + color: #777; } + +p { + margin: 0 0 1em; } + +ul, ol { + padding: 0 0 0 2em; + margin: 0 0 0.85em; } + +blockquote { + margin: 0 0 0.85em; + padding: 0 15px; + color: #858585; + border-left: 4px solid #e5e5e5; } + +img { + max-width: 100%; } + +a { + color: #4183c4; + text-decoration: none; } + a:hover, a:focus { + outline: 0; + text-decoration: underline; } + a.discouraged { + text-decoration: line-through; } + a.discouraged:hover, a.discouraged:focus { + text-decoration: underline line-through; } + +table { + background: #fff; + width: 100%; + border-collapse: collapse; + border-spacing: 0; + overflow: auto; + margin: 0 0 0.85em; } + +tr:nth-child(2n) { + background-color: #fbfbfb; } + +th, td { + padding: 6px 13px; + border: 1px solid #ddd; } + +hr { + height: 1px; + border: none; + background-color: #ddd; } + +pre { + margin: 0 0 1.275em; + padding: .85em 1em; + overflow: auto; + background: #f7f7f7; + font-size: .85em; + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +code { + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +.item-container p > code, .item-container li > code, .top-matter p > code, .top-matter li > code { + background: #f7f7f7; + padding: .2em; } + .item-container p > code:before, .item-container p > code:after, .item-container li > code:before, .item-container li > code:after, .top-matter p > code:before, .top-matter p > code:after, .top-matter li > code:before, .top-matter li > code:after { + letter-spacing: -.2em; + content: "\00a0"; } + +pre code { + padding: 0; + white-space: pre; } + +.content-wrapper { + display: flex; + flex-direction: column; } + @media (min-width: 768px) { + .content-wrapper { + flex-direction: row; } } +.header { + display: flex; + padding: 8px; + font-size: 0.875em; + background: #444; + color: #999; } + +.header-col { + margin: 0; + padding: 0 8px; } + +.header-col--primary { + flex: 1; } + +.header-link { + color: #fff; } + +.header-icon { + padding-right: 6px; + vertical-align: -4px; + height: 16px; } + +.breadcrumbs { + font-size: 0.875em; + padding: 8px 16px; + margin: 0; + background: #fbfbfb; + border-bottom: 1px solid #ddd; } + +.carat { + height: 10px; + margin: 0 5px; } + +.navigation { + order: 2; } + @media (min-width: 768px) { + .navigation { + order: 1; + width: 25%; + max-width: 300px; + padding-bottom: 64px; + overflow: hidden; + word-wrap: normal; + background: #fbfbfb; + border-right: 1px solid #ddd; } } +.nav-groups { + list-style-type: none; + padding-left: 0; } + +.nav-group-name { + border-bottom: 1px solid #ddd; + padding: 8px 0 8px 16px; } + +.nav-group-name-link { + color: #333; } + +.nav-group-tasks { + margin: 8px 0; + padding: 0 0 0 8px; } + +.nav-group-task { + font-size: 1em; + list-style-type: none; + white-space: nowrap; } + +.nav-group-task-link { + color: #808080; } + +.main-content { + order: 1; } + @media (min-width: 768px) { + .main-content { + order: 2; + flex: 1; + padding-bottom: 60px; } } +.section { + padding: 0 32px; + border-bottom: 1px solid #ddd; } + +.section-content { + max-width: 834px; + margin: 0 auto; + padding: 16px 0; } + +.section-name { + color: #666; + display: block; } + .section-name p { + margin-bottom: inherit; } + +.declaration .highlight { + overflow-x: initial; + padding: 8px 0; + margin: 0; + background-color: transparent; + border: none; } + +.task-group-section { + border-top: 1px solid #ddd; } + +.task-group { + padding-top: 0px; } + +.task-name-container a[name]:before { + content: ""; + display: block; } + +.section-name-container { + position: relative; } + .section-name-container .section-name-link { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + margin-bottom: 0; } + .section-name-container .section-name { + position: relative; + pointer-events: none; + z-index: 1; } + .section-name-container .section-name a { + pointer-events: auto; } + +.item-container { + padding: 0; } + +.item { + padding-top: 8px; + width: 100%; + list-style-type: none; } + .item a[name]:before { + content: ""; + display: block; } + .item .token, .item .direct-link { + display: inline-block; + text-indent: -20px; + padding-left: 3px; + margin-left: 20px; + font-size: 1rem; } + .item .declaration-note { + font-size: .85em; + color: #808080; + font-style: italic; } + +.pointer-container { + border-bottom: 1px solid #ddd; + left: -23px; + padding-bottom: 13px; + position: relative; + width: 110%; } + +.pointer { + left: 21px; + top: 7px; + display: block; + position: absolute; + width: 12px; + height: 12px; + border-left: 1px solid #ddd; + border-top: 1px solid #ddd; + background: #fff; + transform: rotate(45deg); } + +.height-container { + display: none; + position: relative; + width: 100%; + overflow: hidden; } + .height-container .section { + background: #fff; + border: 1px solid #ddd; + border-top-width: 0; + padding-top: 10px; + padding-bottom: 5px; + padding: 8px 16px; } + +.aside, .language { + padding: 6px 12px; + margin: 12px 0; + border-left: 5px solid #dddddd; + overflow-y: hidden; } + .aside .aside-title, .language .aside-title { + font-size: 9px; + letter-spacing: 2px; + text-transform: uppercase; + padding-bottom: 0; + margin: 0; + color: #aaa; + -webkit-user-select: none; } + .aside p:last-child, .language p:last-child { + margin-bottom: 0; } + +.language { + border-left: 5px solid #cde9f4; } + .language .aside-title { + color: #4183c4; } + +.aside-warning, .aside-deprecated, .aside-unavailable { + border-left: 5px solid #ff6666; } + .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { + color: #ff0000; } + +.graybox { + border-collapse: collapse; + width: 100%; } + .graybox p { + margin: 0; + word-break: break-word; + min-width: 50px; } + .graybox td { + border: 1px solid #ddd; + padding: 5px 25px 5px 10px; + vertical-align: middle; } + .graybox tr td:first-of-type { + text-align: right; + padding: 7px; + vertical-align: top; + word-break: normal; + width: 40px; } + +.slightly-smaller { + font-size: 0.9em; } + +.footer { + padding: 8px 16px; + background: #444; + color: #ddd; + font-size: 0.8em; } + .footer p { + margin: 8px 0; } + .footer a { + color: #fff; } + +html.dash .header, html.dash .breadcrumbs, html.dash .navigation { + display: none; } + +html.dash .height-container { + display: block; } + +form[role=search] input { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 24px; + padding: 0 10px; + margin: 0; + border: none; + border-radius: 1em; } + .loading form[role=search] input { + background: white url(http://github.com/img/spinner.gif) center right 4px no-repeat; } + +form[role=search] .tt-menu { + margin: 0; + min-width: 300px; + background: #fbfbfb; + color: #333; + border: 1px solid #ddd; } + +form[role=search] .tt-highlight { + font-weight: bold; } + +form[role=search] .tt-suggestion { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 0 8px; } + form[role=search] .tt-suggestion span { + display: table-cell; + white-space: nowrap; } + form[role=search] .tt-suggestion .doc-parent-name { + width: 100%; + text-align: right; + font-weight: normal; + font-size: 0.9em; + padding-left: 16px; } + +form[role=search] .tt-suggestion:hover, +form[role=search] .tt-suggestion.tt-cursor { + cursor: pointer; + background-color: #4183c4; + color: #fff; } + +form[role=search] .tt-suggestion:hover .doc-parent-name, +form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { + color: #fff; } diff --git a/docs/1.5.0/AsyncHTTPClient/img/carat.png b/docs/1.5.0/AsyncHTTPClient/img/carat.png new file mode 100755 index 000000000..29d2f7fd4 Binary files /dev/null and b/docs/1.5.0/AsyncHTTPClient/img/carat.png differ diff --git a/docs/1.5.0/AsyncHTTPClient/img/dash.png b/docs/1.5.0/AsyncHTTPClient/img/dash.png new file mode 100755 index 000000000..6f694c7a0 Binary files /dev/null and b/docs/1.5.0/AsyncHTTPClient/img/dash.png differ diff --git a/docs/1.5.0/AsyncHTTPClient/img/gh.png b/docs/1.5.0/AsyncHTTPClient/img/gh.png new file mode 100755 index 000000000..628da97c7 Binary files /dev/null and b/docs/1.5.0/AsyncHTTPClient/img/gh.png differ diff --git a/docs/1.5.0/AsyncHTTPClient/img/spinner.gif b/docs/1.5.0/AsyncHTTPClient/img/spinner.gif new file mode 100644 index 000000000..e3038d0a4 Binary files /dev/null and b/docs/1.5.0/AsyncHTTPClient/img/spinner.gif differ diff --git a/docs/1.5.0/AsyncHTTPClient/index.html b/docs/1.5.0/AsyncHTTPClient/index.html new file mode 100644 index 000000000..a09eb32e3 --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/index.html @@ -0,0 +1,168 @@ + + + + AsyncHTTPClient Reference + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.0 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+ +

AsyncHTTPClient Docs

+ +

AsyncHTTPClient is a Swift HTTP Client package.

+ +

To get started with AsyncHTTPClient, import AsyncHTTPClient. The +most important type is HTTPClient +which you can use to emit log messages.

+ +
+
+ + +
+
+ + + + diff --git a/docs/1.5.0/AsyncHTTPClient/js/jazzy.js b/docs/1.5.0/AsyncHTTPClient/js/jazzy.js new file mode 100755 index 000000000..1e55d6ef0 --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/js/jazzy.js @@ -0,0 +1,70 @@ +window.jazzy = {'docset': false} +if (typeof window.dash != 'undefined') { + document.documentElement.className += ' dash' + window.jazzy.docset = true +} +if (navigator.userAgent.match(/xcode/i)) { + document.documentElement.className += ' xcode' + window.jazzy.docset = true +} + +function toggleItem($link, $content) { + var animationDuration = 300; + $link.toggleClass('token-open'); + $content.slideToggle(animationDuration); +} + +function itemLinkToContent($link) { + return $link.parent().parent().next(); +} + +// On doc load + hash-change, open any targetted item +function openCurrentItemIfClosed() { + if (window.jazzy.docset) { + return; + } + var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); + $content = itemLinkToContent($link); + if ($content.is(':hidden')) { + toggleItem($link, $content); + } +} + +$(openCurrentItemIfClosed); +$(window).on('hashchange', openCurrentItemIfClosed); + +// On item link ('token') click, toggle its discussion +$('.token').on('click', function(event) { + if (window.jazzy.docset) { + return; + } + var $link = $(this); + toggleItem($link, itemLinkToContent($link)); + + // Keeps the document from jumping to the hash. + var href = $link.attr('href'); + if (history.pushState) { + history.pushState({}, '', href); + } else { + location.hash = href; + } + event.preventDefault(); +}); + +// Clicks on links to the current, closed, item need to open the item +$("a:not('.token')").on('click', function() { + if (location == this.href) { + openCurrentItemIfClosed(); + } +}); + +// KaTeX rendering +if ("katex" in window) { + $($('.math').each( (_, element) => { + katex.render(element.textContent, element, { + displayMode: $(element).hasClass('m-block'), + throwOnError: false, + trust: true + }); + })) +} diff --git a/docs/1.5.0/AsyncHTTPClient/js/jazzy.search.js b/docs/1.5.0/AsyncHTTPClient/js/jazzy.search.js new file mode 100644 index 000000000..e3d1ab905 --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/js/jazzy.search.js @@ -0,0 +1,70 @@ +$(function(){ + var $typeahead = $('[data-typeahead]'); + var $form = $typeahead.parents('form'); + var searchURL = $form.attr('action'); + + function displayTemplate(result) { + return result.name; + } + + function suggestionTemplate(result) { + var t = '
'; + t += '' + result.name + ''; + if (result.parent_name) { + t += '' + result.parent_name + ''; + } + t += '
'; + return t; + } + + $typeahead.one('focus', function() { + $form.addClass('loading'); + + $.getJSON(searchURL).then(function(searchData) { + const searchIndex = lunr(function() { + this.ref('url'); + this.field('name'); + this.field('abstract'); + for (const [url, doc] of Object.entries(searchData)) { + this.add({url: url, name: doc.name, abstract: doc.abstract}); + } + }); + + $typeahead.typeahead( + { + highlight: true, + minLength: 3, + autoselect: true + }, + { + limit: 10, + display: displayTemplate, + templates: { suggestion: suggestionTemplate }, + source: function(query, sync) { + const lcSearch = query.toLowerCase(); + const results = searchIndex.query(function(q) { + q.term(lcSearch, { boost: 100 }); + q.term(lcSearch, { + boost: 10, + wildcard: lunr.Query.wildcard.TRAILING + }); + }).map(function(result) { + var doc = searchData[result.ref]; + doc.url = result.ref; + return doc; + }); + sync(results); + } + } + ); + $form.removeClass('loading'); + $typeahead.trigger('focus'); + }); + }); + + var baseURL = searchURL.slice(0, -"search.json".length); + + $typeahead.on('typeahead:select', function(e, result) { + window.location = baseURL + result.url; + }); +}); diff --git a/docs/1.5.0/AsyncHTTPClient/js/jquery.min.js b/docs/1.5.0/AsyncHTTPClient/js/jquery.min.js new file mode 100644 index 000000000..c4c6022f2 --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 00){var c=e.utils.clone(r)||{};c.position=[a,l],c.index=s.length,s.push(new e.Token(i.slice(a,o),c))}a=o+1}}return s},e.tokenizer.separator=/[\s\-]+/,e.Pipeline=function(){this._stack=[]},e.Pipeline.registeredFunctions=Object.create(null),e.Pipeline.registerFunction=function(t,r){r in this.registeredFunctions&&e.utils.warn("Overwriting existing registered function: "+r),t.label=r,e.Pipeline.registeredFunctions[t.label]=t},e.Pipeline.warnIfFunctionNotRegistered=function(t){var r=t.label&&t.label in this.registeredFunctions;r||e.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",t)},e.Pipeline.load=function(t){var r=new e.Pipeline;return t.forEach(function(t){var i=e.Pipeline.registeredFunctions[t];if(!i)throw new Error("Cannot load unregistered function: "+t);r.add(i)}),r},e.Pipeline.prototype.add=function(){var t=Array.prototype.slice.call(arguments);t.forEach(function(t){e.Pipeline.warnIfFunctionNotRegistered(t),this._stack.push(t)},this)},e.Pipeline.prototype.after=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,r)},e.Pipeline.prototype.before=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");this._stack.splice(i,0,r)},e.Pipeline.prototype.remove=function(e){var t=this._stack.indexOf(e);t!=-1&&this._stack.splice(t,1)},e.Pipeline.prototype.run=function(e){for(var t=this._stack.length,r=0;r1&&(se&&(r=n),s!=e);)i=r-t,n=t+Math.floor(i/2),s=this.elements[2*n];return s==e?2*n:s>e?2*n:sa?l+=2:o==a&&(t+=r[u+1]*i[l+1],u+=2,l+=2);return t},e.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},e.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),t=1,r=0;t0){var o,a=s.str.charAt(0);a in s.node.edges?o=s.node.edges[a]:(o=new e.TokenSet,s.node.edges[a]=o),1==s.str.length&&(o["final"]=!0),n.push({node:o,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(0!=s.editsRemaining){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new e.TokenSet;s.node.edges["*"]=u}if(0==s.str.length&&(u["final"]=!0),n.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&n.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),1==s.str.length&&(s.node["final"]=!0),s.str.length>=1){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new e.TokenSet;s.node.edges["*"]=l}1==s.str.length&&(l["final"]=!0),n.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var c,h=s.str.charAt(0),d=s.str.charAt(1);d in s.node.edges?c=s.node.edges[d]:(c=new e.TokenSet,s.node.edges[d]=c),1==s.str.length&&(c["final"]=!0),n.push({node:c,editsRemaining:s.editsRemaining-1,str:h+s.str.slice(2)})}}}return i},e.TokenSet.fromString=function(t){for(var r=new e.TokenSet,i=r,n=0,s=t.length;n=e;t--){var r=this.uncheckedNodes[t],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r["char"]]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}},e.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},e.Index.prototype.search=function(t){return this.query(function(r){var i=new e.QueryParser(t,r);i.parse()})},e.Index.prototype.query=function(t){for(var r=new e.Query(this.fields),i=Object.create(null),n=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),u=0;u1?this._b=1:this._b=e},e.Builder.prototype.k1=function(e){this._k1=e},e.Builder.prototype.add=function(t,r){var i=t[this._ref],n=Object.keys(this._fields);this._documents[i]=r||{},this.documentCount+=1;for(var s=0;s=this.length)return e.QueryLexer.EOS;var t=this.str.charAt(this.pos);return this.pos+=1,t},e.QueryLexer.prototype.width=function(){return this.pos-this.start},e.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},e.QueryLexer.prototype.backup=function(){this.pos-=1},e.QueryLexer.prototype.acceptDigitRun=function(){var t,r;do t=this.next(),r=t.charCodeAt(0);while(r>47&&r<58);t!=e.QueryLexer.EOS&&this.backup()},e.QueryLexer.prototype.more=function(){return this.pos1&&(t.backup(),t.emit(e.QueryLexer.TERM)),t.ignore(),t.more())return e.QueryLexer.lexText},e.QueryLexer.lexEditDistance=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.EDIT_DISTANCE),e.QueryLexer.lexText},e.QueryLexer.lexBoost=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.BOOST),e.QueryLexer.lexText},e.QueryLexer.lexEOS=function(t){t.width()>0&&t.emit(e.QueryLexer.TERM)},e.QueryLexer.termSeparator=e.tokenizer.separator,e.QueryLexer.lexText=function(t){for(;;){var r=t.next();if(r==e.QueryLexer.EOS)return e.QueryLexer.lexEOS;if(92!=r.charCodeAt(0)){if(":"==r)return e.QueryLexer.lexField;if("~"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexEditDistance;if("^"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexBoost;if("+"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if("-"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if(r.match(e.QueryLexer.termSeparator))return e.QueryLexer.lexTerm}else t.escapeCharacter()}},e.QueryParser=function(t,r){this.lexer=new e.QueryLexer(t),this.query=r,this.currentClause={},this.lexemeIdx=0},e.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var t=e.QueryParser.parseClause;t;)t=t(this);return this.query},e.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},e.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},e.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},e.QueryParser.parseClause=function(t){var r=t.peekLexeme();if(void 0!=r)switch(r.type){case e.QueryLexer.PRESENCE:return e.QueryParser.parsePresence;case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expected either a field or a term, found "+r.type;throw r.str.length>=1&&(i+=" with value '"+r.str+"'"),new e.QueryParseError(i,r.start,r.end)}},e.QueryParser.parsePresence=function(t){var r=t.consumeLexeme();if(void 0!=r){switch(r.str){case"-":t.currentClause.presence=e.Query.presence.PROHIBITED;break;case"+":t.currentClause.presence=e.Query.presence.REQUIRED;break;default:var i="unrecognised presence operator'"+r.str+"'";throw new e.QueryParseError(i,r.start,r.end)}var n=t.peekLexeme();if(void 0==n){var i="expecting term or field, found nothing";throw new e.QueryParseError(i,r.start,r.end)}switch(n.type){case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expecting term or field, found '"+n.type+"'";throw new e.QueryParseError(i,n.start,n.end)}}},e.QueryParser.parseField=function(t){var r=t.consumeLexeme();if(void 0!=r){if(t.query.allFields.indexOf(r.str)==-1){var i=t.query.allFields.map(function(e){return"'"+e+"'"}).join(", "),n="unrecognised field '"+r.str+"', possible fields: "+i;throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.fields=[r.str];var s=t.peekLexeme();if(void 0==s){var n="expecting term, found nothing";throw new e.QueryParseError(n,r.start,r.end)}switch(s.type){case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var n="expecting term, found '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseTerm=function(t){var r=t.consumeLexeme();if(void 0!=r){t.currentClause.term=r.str.toLowerCase(),r.str.indexOf("*")!=-1&&(t.currentClause.usePipeline=!1);var i=t.peekLexeme();if(void 0==i)return void t.nextClause();switch(i.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+i.type+"'";throw new e.QueryParseError(n,i.start,i.end)}}},e.QueryParser.parseEditDistance=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="edit distance must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.editDistance=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseBoost=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="boost must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.boost=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():e.lunr=t()}(this,function(){return e})}(); diff --git a/docs/1.5.0/AsyncHTTPClient/js/typeahead.jquery.js b/docs/1.5.0/AsyncHTTPClient/js/typeahead.jquery.js new file mode 100644 index 000000000..3a2d2ab03 --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/js/typeahead.jquery.js @@ -0,0 +1,1694 @@ +/*! + * typeahead.js 1.3.1 + * https://github.com/corejavascript/typeahead.js + * Copyright 2013-2020 Twitter, Inc. and other contributors; Licensed MIT + */ + + +(function(root, factory) { + if (typeof define === "function" && define.amd) { + define([ "jquery" ], function(a0) { + return factory(a0); + }); + } else if (typeof module === "object" && module.exports) { + module.exports = factory(require("jquery")); + } else { + factory(root["jQuery"]); + } +})(this, function($) { + var _ = function() { + "use strict"; + return { + isMsie: function() { + return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false; + }, + isBlankString: function(str) { + return !str || /^\s*$/.test(str); + }, + escapeRegExChars: function(str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + }, + isString: function(obj) { + return typeof obj === "string"; + }, + isNumber: function(obj) { + return typeof obj === "number"; + }, + isArray: $.isArray, + isFunction: $.isFunction, + isObject: $.isPlainObject, + isUndefined: function(obj) { + return typeof obj === "undefined"; + }, + isElement: function(obj) { + return !!(obj && obj.nodeType === 1); + }, + isJQuery: function(obj) { + return obj instanceof $; + }, + toStr: function toStr(s) { + return _.isUndefined(s) || s === null ? "" : s + ""; + }, + bind: $.proxy, + each: function(collection, cb) { + $.each(collection, reverseArgs); + function reverseArgs(index, value) { + return cb(value, index); + } + }, + map: $.map, + filter: $.grep, + every: function(obj, test) { + var result = true; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (!(result = test.call(null, val, key, obj))) { + return false; + } + }); + return !!result; + }, + some: function(obj, test) { + var result = false; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (result = test.call(null, val, key, obj)) { + return false; + } + }); + return !!result; + }, + mixin: $.extend, + identity: function(x) { + return x; + }, + clone: function(obj) { + return $.extend(true, {}, obj); + }, + getIdGenerator: function() { + var counter = 0; + return function() { + return counter++; + }; + }, + templatify: function templatify(obj) { + return $.isFunction(obj) ? obj : template; + function template() { + return String(obj); + } + }, + defer: function(fn) { + setTimeout(fn, 0); + }, + debounce: function(func, wait, immediate) { + var timeout, result; + return function() { + var context = this, args = arguments, later, callNow; + later = function() { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + } + }; + callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + } + return result; + }; + }, + throttle: function(func, wait) { + var context, args, timeout, result, previous, later; + previous = 0; + later = function() { + previous = new Date(); + timeout = null; + result = func.apply(context, args); + }; + return function() { + var now = new Date(), remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + } else if (!timeout) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }, + stringify: function(val) { + return _.isString(val) ? val : JSON.stringify(val); + }, + guid: function() { + function _p8(s) { + var p = (Math.random().toString(16) + "000000000").substr(2, 8); + return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p; + } + return "tt-" + _p8() + _p8(true) + _p8(true) + _p8(); + }, + noop: function() {} + }; + }(); + var WWW = function() { + "use strict"; + var defaultClassNames = { + wrapper: "twitter-typeahead", + input: "tt-input", + hint: "tt-hint", + menu: "tt-menu", + dataset: "tt-dataset", + suggestion: "tt-suggestion", + selectable: "tt-selectable", + empty: "tt-empty", + open: "tt-open", + cursor: "tt-cursor", + highlight: "tt-highlight" + }; + return build; + function build(o) { + var www, classes; + classes = _.mixin({}, defaultClassNames, o); + www = { + css: buildCss(), + classes: classes, + html: buildHtml(classes), + selectors: buildSelectors(classes) + }; + return { + css: www.css, + html: www.html, + classes: www.classes, + selectors: www.selectors, + mixin: function(o) { + _.mixin(o, www); + } + }; + } + function buildHtml(c) { + return { + wrapper: '', + menu: '
' + }; + } + function buildSelectors(classes) { + var selectors = {}; + _.each(classes, function(v, k) { + selectors[k] = "." + v; + }); + return selectors; + } + function buildCss() { + var css = { + wrapper: { + position: "relative", + display: "inline-block" + }, + hint: { + position: "absolute", + top: "0", + left: "0", + borderColor: "transparent", + boxShadow: "none", + opacity: "1" + }, + input: { + position: "relative", + verticalAlign: "top", + backgroundColor: "transparent" + }, + inputWithNoHint: { + position: "relative", + verticalAlign: "top" + }, + menu: { + position: "absolute", + top: "100%", + left: "0", + zIndex: "100", + display: "none" + }, + ltr: { + left: "0", + right: "auto" + }, + rtl: { + left: "auto", + right: " 0" + } + }; + if (_.isMsie()) { + _.mixin(css.input, { + backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" + }); + } + return css; + } + }(); + var EventBus = function() { + "use strict"; + var namespace, deprecationMap; + namespace = "typeahead:"; + deprecationMap = { + render: "rendered", + cursorchange: "cursorchanged", + select: "selected", + autocomplete: "autocompleted" + }; + function EventBus(o) { + if (!o || !o.el) { + $.error("EventBus initialized without el"); + } + this.$el = $(o.el); + } + _.mixin(EventBus.prototype, { + _trigger: function(type, args) { + var $e = $.Event(namespace + type); + this.$el.trigger.call(this.$el, $e, args || []); + return $e; + }, + before: function(type) { + var args, $e; + args = [].slice.call(arguments, 1); + $e = this._trigger("before" + type, args); + return $e.isDefaultPrevented(); + }, + trigger: function(type) { + var deprecatedType; + this._trigger(type, [].slice.call(arguments, 1)); + if (deprecatedType = deprecationMap[type]) { + this._trigger(deprecatedType, [].slice.call(arguments, 1)); + } + } + }); + return EventBus; + }(); + var EventEmitter = function() { + "use strict"; + var splitter = /\s+/, nextTick = getNextTick(); + return { + onSync: onSync, + onAsync: onAsync, + off: off, + trigger: trigger + }; + function on(method, types, cb, context) { + var type; + if (!cb) { + return this; + } + types = types.split(splitter); + cb = context ? bindContext(cb, context) : cb; + this._callbacks = this._callbacks || {}; + while (type = types.shift()) { + this._callbacks[type] = this._callbacks[type] || { + sync: [], + async: [] + }; + this._callbacks[type][method].push(cb); + } + return this; + } + function onAsync(types, cb, context) { + return on.call(this, "async", types, cb, context); + } + function onSync(types, cb, context) { + return on.call(this, "sync", types, cb, context); + } + function off(types) { + var type; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + while (type = types.shift()) { + delete this._callbacks[type]; + } + return this; + } + function trigger(types) { + var type, callbacks, args, syncFlush, asyncFlush; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + args = [].slice.call(arguments, 1); + while ((type = types.shift()) && (callbacks = this._callbacks[type])) { + syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args)); + asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args)); + syncFlush() && nextTick(asyncFlush); + } + return this; + } + function getFlush(callbacks, context, args) { + return flush; + function flush() { + var cancelled; + for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) { + cancelled = callbacks[i].apply(context, args) === false; + } + return !cancelled; + } + } + function getNextTick() { + var nextTickFn; + if (window.setImmediate) { + nextTickFn = function nextTickSetImmediate(fn) { + setImmediate(function() { + fn(); + }); + }; + } else { + nextTickFn = function nextTickSetTimeout(fn) { + setTimeout(function() { + fn(); + }, 0); + }; + } + return nextTickFn; + } + function bindContext(fn, context) { + return fn.bind ? fn.bind(context) : function() { + fn.apply(context, [].slice.call(arguments, 0)); + }; + } + }(); + var highlight = function(doc) { + "use strict"; + var defaults = { + node: null, + pattern: null, + tagName: "strong", + className: null, + wordsOnly: false, + caseSensitive: false, + diacriticInsensitive: false + }; + var accented = { + A: "[AaªÀ-Åà-åĀ-ąǍǎȀ-ȃȦȧᴬᵃḀḁẚẠ-ảₐ℀℁℻⒜Ⓐⓐ㍱-㍴㎀-㎄㎈㎉㎩-㎯㏂㏊㏟㏿Aa]", + B: "[BbᴮᵇḂ-ḇℬ⒝Ⓑⓑ㍴㎅-㎇㏃㏈㏔㏝Bb]", + C: "[CcÇçĆ-čᶜ℀ℂ℃℅℆ℭⅭⅽ⒞Ⓒⓒ㍶㎈㎉㎝㎠㎤㏄-㏇Cc]", + D: "[DdĎďDŽ-džDZ-dzᴰᵈḊ-ḓⅅⅆⅮⅾ⒟Ⓓⓓ㋏㍲㍷-㍹㎗㎭-㎯㏅㏈Dd]", + E: "[EeÈ-Ëè-ëĒ-ěȄ-ȇȨȩᴱᵉḘ-ḛẸ-ẽₑ℡ℯℰⅇ⒠Ⓔⓔ㉐㋍㋎Ee]", + F: "[FfᶠḞḟ℉ℱ℻⒡Ⓕⓕ㎊-㎌㎙ff-fflFf]", + G: "[GgĜ-ģǦǧǴǵᴳᵍḠḡℊ⒢Ⓖⓖ㋌㋍㎇㎍-㎏㎓㎬㏆㏉㏒㏿Gg]", + H: "[HhĤĥȞȟʰᴴḢ-ḫẖℋ-ℎ⒣Ⓗⓗ㋌㍱㎐-㎔㏊㏋㏗Hh]", + I: "[IiÌ-Ïì-ïĨ-İIJijǏǐȈ-ȋᴵᵢḬḭỈ-ịⁱℐℑℹⅈⅠ-ⅣⅥ-ⅨⅪⅫⅰ-ⅳⅵ-ⅸⅺⅻ⒤Ⓘⓘ㍺㏌㏕fiffiIi]", + J: "[JjIJ-ĵLJ-njǰʲᴶⅉ⒥ⒿⓙⱼJj]", + K: "[KkĶķǨǩᴷᵏḰ-ḵK⒦Ⓚⓚ㎄㎅㎉㎏㎑㎘㎞㎢㎦㎪㎸㎾㏀㏆㏍-㏏Kk]", + L: "[LlĹ-ŀLJ-ljˡᴸḶḷḺ-ḽℒℓ℡Ⅼⅼ⒧Ⓛⓛ㋏㎈㎉㏐-㏓㏕㏖㏿flfflLl]", + M: "[MmᴹᵐḾ-ṃ℠™ℳⅯⅿ⒨Ⓜⓜ㍷-㍹㎃㎆㎎㎒㎖㎙-㎨㎫㎳㎷㎹㎽㎿㏁㏂㏎㏐㏔-㏖㏘㏙㏞㏟Mm]", + N: "[NnÑñŃ-ʼnNJ-njǸǹᴺṄ-ṋⁿℕ№⒩Ⓝⓝ㎁㎋㎚㎱㎵㎻㏌㏑Nn]", + O: "[OoºÒ-Öò-öŌ-őƠơǑǒǪǫȌ-ȏȮȯᴼᵒỌ-ỏₒ℅№ℴ⒪Ⓞⓞ㍵㏇㏒㏖Oo]", + P: "[PpᴾᵖṔ-ṗℙ⒫Ⓟⓟ㉐㍱㍶㎀㎊㎩-㎬㎰㎴㎺㏋㏗-㏚Pp]", + Q: "[Qqℚ⒬Ⓠⓠ㏃Qq]", + R: "[RrŔ-řȐ-ȓʳᴿᵣṘ-ṛṞṟ₨ℛ-ℝ⒭Ⓡⓡ㋍㍴㎭-㎯㏚㏛Rr]", + S: "[SsŚ-šſȘșˢṠ-ṣ₨℁℠⒮Ⓢⓢ㎧㎨㎮-㎳㏛㏜stSs]", + T: "[TtŢ-ťȚțᵀᵗṪ-ṱẗ℡™⒯Ⓣⓣ㉐㋏㎔㏏ſtstTt]", + U: "[UuÙ-Üù-üŨ-ųƯưǓǔȔ-ȗᵁᵘᵤṲ-ṷỤ-ủ℆⒰Ⓤⓤ㍳㍺Uu]", + V: "[VvᵛᵥṼ-ṿⅣ-Ⅷⅳ-ⅷ⒱Ⓥⓥⱽ㋎㍵㎴-㎹㏜㏞Vv]", + W: "[WwŴŵʷᵂẀ-ẉẘ⒲Ⓦⓦ㎺-㎿㏝Ww]", + X: "[XxˣẊ-ẍₓ℻Ⅸ-Ⅻⅸ-ⅻ⒳Ⓧⓧ㏓Xx]", + Y: "[YyÝýÿŶ-ŸȲȳʸẎẏẙỲ-ỹ⒴Ⓨⓨ㏉Yy]", + Z: "[ZzŹ-žDZ-dzᶻẐ-ẕℤℨ⒵Ⓩⓩ㎐-㎔Zz]" + }; + return function hightlight(o) { + var regex; + o = _.mixin({}, defaults, o); + if (!o.node || !o.pattern) { + return; + } + o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; + regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly, o.diacriticInsensitive); + traverse(o.node, hightlightTextNode); + function hightlightTextNode(textNode) { + var match, patternNode, wrapperNode; + if (match = regex.exec(textNode.data)) { + wrapperNode = doc.createElement(o.tagName); + o.className && (wrapperNode.className = o.className); + patternNode = textNode.splitText(match.index); + patternNode.splitText(match[0].length); + wrapperNode.appendChild(patternNode.cloneNode(true)); + textNode.parentNode.replaceChild(wrapperNode, patternNode); + } + return !!match; + } + function traverse(el, hightlightTextNode) { + var childNode, TEXT_NODE_TYPE = 3; + for (var i = 0; i < el.childNodes.length; i++) { + childNode = el.childNodes[i]; + if (childNode.nodeType === TEXT_NODE_TYPE) { + i += hightlightTextNode(childNode) ? 1 : 0; + } else { + traverse(childNode, hightlightTextNode); + } + } + } + }; + function accent_replacer(chr) { + return accented[chr.toUpperCase()] || chr; + } + function getRegex(patterns, caseSensitive, wordsOnly, diacriticInsensitive) { + var escapedPatterns = [], regexStr; + for (var i = 0, len = patterns.length; i < len; i++) { + var escapedWord = _.escapeRegExChars(patterns[i]); + if (diacriticInsensitive) { + escapedWord = escapedWord.replace(/\S/g, accent_replacer); + } + escapedPatterns.push(escapedWord); + } + regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")"; + return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); + } + }(window.document); + var Input = function() { + "use strict"; + var specialKeyCodeMap; + specialKeyCodeMap = { + 9: "tab", + 27: "esc", + 37: "left", + 39: "right", + 13: "enter", + 38: "up", + 40: "down" + }; + function Input(o, www) { + var id; + o = o || {}; + if (!o.input) { + $.error("input is missing"); + } + www.mixin(this); + this.$hint = $(o.hint); + this.$input = $(o.input); + this.$menu = $(o.menu); + id = this.$input.attr("id") || _.guid(); + this.$menu.attr("id", id + "_listbox"); + this.$hint.attr({ + "aria-hidden": true + }); + this.$input.attr({ + "aria-owns": id + "_listbox", + role: "combobox", + "aria-autocomplete": "list", + "aria-expanded": false + }); + this.query = this.$input.val(); + this.queryWhenFocused = this.hasFocus() ? this.query : null; + this.$overflowHelper = buildOverflowHelper(this.$input); + this._checkLanguageDirection(); + if (this.$hint.length === 0) { + this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; + } + this.onSync("cursorchange", this._updateDescendent); + } + Input.normalizeQuery = function(str) { + return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " "); + }; + _.mixin(Input.prototype, EventEmitter, { + _onBlur: function onBlur() { + this.resetInputValue(); + this.trigger("blurred"); + }, + _onFocus: function onFocus() { + this.queryWhenFocused = this.query; + this.trigger("focused"); + }, + _onKeydown: function onKeydown($e) { + var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; + this._managePreventDefault(keyName, $e); + if (keyName && this._shouldTrigger(keyName, $e)) { + this.trigger(keyName + "Keyed", $e); + } + }, + _onInput: function onInput() { + this._setQuery(this.getInputValue()); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + _managePreventDefault: function managePreventDefault(keyName, $e) { + var preventDefault; + switch (keyName) { + case "up": + case "down": + preventDefault = !withModifier($e); + break; + + default: + preventDefault = false; + } + preventDefault && $e.preventDefault(); + }, + _shouldTrigger: function shouldTrigger(keyName, $e) { + var trigger; + switch (keyName) { + case "tab": + trigger = !withModifier($e); + break; + + default: + trigger = true; + } + return trigger; + }, + _checkLanguageDirection: function checkLanguageDirection() { + var dir = (this.$input.css("direction") || "ltr").toLowerCase(); + if (this.dir !== dir) { + this.dir = dir; + this.$hint.attr("dir", dir); + this.trigger("langDirChanged", dir); + } + }, + _setQuery: function setQuery(val, silent) { + var areEquivalent, hasDifferentWhitespace; + areEquivalent = areQueriesEquivalent(val, this.query); + hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false; + this.query = val; + if (!silent && !areEquivalent) { + this.trigger("queryChanged", this.query); + } else if (!silent && hasDifferentWhitespace) { + this.trigger("whitespaceChanged", this.query); + } + }, + _updateDescendent: function updateDescendent(event, id) { + this.$input.attr("aria-activedescendant", id); + }, + bind: function() { + var that = this, onBlur, onFocus, onKeydown, onInput; + onBlur = _.bind(this._onBlur, this); + onFocus = _.bind(this._onFocus, this); + onKeydown = _.bind(this._onKeydown, this); + onInput = _.bind(this._onInput, this); + this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown); + if (!_.isMsie() || _.isMsie() > 9) { + this.$input.on("input.tt", onInput); + } else { + this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { + if (specialKeyCodeMap[$e.which || $e.keyCode]) { + return; + } + _.defer(_.bind(that._onInput, that, $e)); + }); + } + return this; + }, + focus: function focus() { + this.$input.focus(); + }, + blur: function blur() { + this.$input.blur(); + }, + getLangDir: function getLangDir() { + return this.dir; + }, + getQuery: function getQuery() { + return this.query || ""; + }, + setQuery: function setQuery(val, silent) { + this.setInputValue(val); + this._setQuery(val, silent); + }, + hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() { + return this.query !== this.queryWhenFocused; + }, + getInputValue: function getInputValue() { + return this.$input.val(); + }, + setInputValue: function setInputValue(value) { + this.$input.val(value); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + resetInputValue: function resetInputValue() { + this.setInputValue(this.query); + }, + getHint: function getHint() { + return this.$hint.val(); + }, + setHint: function setHint(value) { + this.$hint.val(value); + }, + clearHint: function clearHint() { + this.setHint(""); + }, + clearHintIfInvalid: function clearHintIfInvalid() { + var val, hint, valIsPrefixOfHint, isValid; + val = this.getInputValue(); + hint = this.getHint(); + valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; + isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow(); + !isValid && this.clearHint(); + }, + hasFocus: function hasFocus() { + return this.$input.is(":focus"); + }, + hasOverflow: function hasOverflow() { + var constraint = this.$input.width() - 2; + this.$overflowHelper.text(this.getInputValue()); + return this.$overflowHelper.width() >= constraint; + }, + isCursorAtEnd: function() { + var valueLength, selectionStart, range; + valueLength = this.$input.val().length; + selectionStart = this.$input[0].selectionStart; + if (_.isNumber(selectionStart)) { + return selectionStart === valueLength; + } else if (document.selection) { + range = document.selection.createRange(); + range.moveStart("character", -valueLength); + return valueLength === range.text.length; + } + return true; + }, + destroy: function destroy() { + this.$hint.off(".tt"); + this.$input.off(".tt"); + this.$overflowHelper.remove(); + this.$hint = this.$input = this.$overflowHelper = $("
"); + }, + setAriaExpanded: function setAriaExpanded(value) { + this.$input.attr("aria-expanded", value); + } + }); + return Input; + function buildOverflowHelper($input) { + return $('').css({ + position: "absolute", + visibility: "hidden", + whiteSpace: "pre", + fontFamily: $input.css("font-family"), + fontSize: $input.css("font-size"), + fontStyle: $input.css("font-style"), + fontVariant: $input.css("font-variant"), + fontWeight: $input.css("font-weight"), + wordSpacing: $input.css("word-spacing"), + letterSpacing: $input.css("letter-spacing"), + textIndent: $input.css("text-indent"), + textRendering: $input.css("text-rendering"), + textTransform: $input.css("text-transform") + }).insertAfter($input); + } + function areQueriesEquivalent(a, b) { + return Input.normalizeQuery(a) === Input.normalizeQuery(b); + } + function withModifier($e) { + return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; + } + }(); + var Dataset = function() { + "use strict"; + var keys, nameGenerator; + keys = { + dataset: "tt-selectable-dataset", + val: "tt-selectable-display", + obj: "tt-selectable-object" + }; + nameGenerator = _.getIdGenerator(); + function Dataset(o, www) { + o = o || {}; + o.templates = o.templates || {}; + o.templates.notFound = o.templates.notFound || o.templates.empty; + if (!o.source) { + $.error("missing source"); + } + if (!o.node) { + $.error("missing node"); + } + if (o.name && !isValidName(o.name)) { + $.error("invalid dataset name: " + o.name); + } + www.mixin(this); + this.highlight = !!o.highlight; + this.name = _.toStr(o.name || nameGenerator()); + this.limit = o.limit || 5; + this.displayFn = getDisplayFn(o.display || o.displayKey); + this.templates = getTemplates(o.templates, this.displayFn); + this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source; + this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async; + this._resetLastSuggestion(); + this.$el = $(o.node).attr("role", "presentation").addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); + } + Dataset.extractData = function extractData(el) { + var $el = $(el); + if ($el.data(keys.obj)) { + return { + dataset: $el.data(keys.dataset) || "", + val: $el.data(keys.val) || "", + obj: $el.data(keys.obj) || null + }; + } + return null; + }; + _.mixin(Dataset.prototype, EventEmitter, { + _overwrite: function overwrite(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (this.async && this.templates.pending) { + this._renderPending(query); + } else if (!this.async && this.templates.notFound) { + this._renderNotFound(query); + } else { + this._empty(); + } + this.trigger("rendered", suggestions, false, this.name); + }, + _append: function append(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length && this.$lastSuggestion.length) { + this._appendSuggestions(query, suggestions); + } else if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (!this.$lastSuggestion.length && this.templates.notFound) { + this._renderNotFound(query); + } + this.trigger("rendered", suggestions, true, this.name); + }, + _renderSuggestions: function renderSuggestions(query, suggestions) { + var $fragment; + $fragment = this._getSuggestionsFragment(query, suggestions); + this.$lastSuggestion = $fragment.children().last(); + this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions)); + }, + _appendSuggestions: function appendSuggestions(query, suggestions) { + var $fragment, $lastSuggestion; + $fragment = this._getSuggestionsFragment(query, suggestions); + $lastSuggestion = $fragment.children().last(); + this.$lastSuggestion.after($fragment); + this.$lastSuggestion = $lastSuggestion; + }, + _renderPending: function renderPending(query) { + var template = this.templates.pending; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _renderNotFound: function renderNotFound(query) { + var template = this.templates.notFound; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _empty: function empty() { + this.$el.empty(); + this._resetLastSuggestion(); + }, + _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) { + var that = this, fragment; + fragment = document.createDocumentFragment(); + _.each(suggestions, function getSuggestionNode(suggestion) { + var $el, context; + context = that._injectQuery(query, suggestion); + $el = $(that.templates.suggestion(context)).data(keys.dataset, that.name).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable); + fragment.appendChild($el[0]); + }); + this.highlight && highlight({ + className: this.classes.highlight, + node: fragment, + pattern: query + }); + return $(fragment); + }, + _getFooter: function getFooter(query, suggestions) { + return this.templates.footer ? this.templates.footer({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _getHeader: function getHeader(query, suggestions) { + return this.templates.header ? this.templates.header({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _resetLastSuggestion: function resetLastSuggestion() { + this.$lastSuggestion = $(); + }, + _injectQuery: function injectQuery(query, obj) { + return _.isObject(obj) ? _.mixin({ + _query: query + }, obj) : obj; + }, + update: function update(query) { + var that = this, canceled = false, syncCalled = false, rendered = 0; + this.cancel(); + this.cancel = function cancel() { + canceled = true; + that.cancel = $.noop; + that.async && that.trigger("asyncCanceled", query, that.name); + }; + this.source(query, sync, async); + !syncCalled && sync([]); + function sync(suggestions) { + if (syncCalled) { + return; + } + syncCalled = true; + suggestions = (suggestions || []).slice(0, that.limit); + rendered = suggestions.length; + that._overwrite(query, suggestions); + if (rendered < that.limit && that.async) { + that.trigger("asyncRequested", query, that.name); + } + } + function async(suggestions) { + suggestions = suggestions || []; + if (!canceled && rendered < that.limit) { + that.cancel = $.noop; + var idx = Math.abs(rendered - that.limit); + rendered += idx; + that._append(query, suggestions.slice(0, idx)); + that.async && that.trigger("asyncReceived", query, that.name); + } + } + }, + cancel: $.noop, + clear: function clear() { + this._empty(); + this.cancel(); + this.trigger("cleared"); + }, + isEmpty: function isEmpty() { + return this.$el.is(":empty"); + }, + destroy: function destroy() { + this.$el = $("
"); + } + }); + return Dataset; + function getDisplayFn(display) { + display = display || _.stringify; + return _.isFunction(display) ? display : displayFn; + function displayFn(obj) { + return obj[display]; + } + } + function getTemplates(templates, displayFn) { + return { + notFound: templates.notFound && _.templatify(templates.notFound), + pending: templates.pending && _.templatify(templates.pending), + header: templates.header && _.templatify(templates.header), + footer: templates.footer && _.templatify(templates.footer), + suggestion: templates.suggestion ? userSuggestionTemplate : suggestionTemplate + }; + function userSuggestionTemplate(context) { + var template = templates.suggestion; + return $(template(context)).attr("id", _.guid()); + } + function suggestionTemplate(context) { + return $('
').attr("id", _.guid()).text(displayFn(context)); + } + } + function isValidName(str) { + return /^[_a-zA-Z0-9-]+$/.test(str); + } + }(); + var Menu = function() { + "use strict"; + function Menu(o, www) { + var that = this; + o = o || {}; + if (!o.node) { + $.error("node is required"); + } + www.mixin(this); + this.$node = $(o.node); + this.query = null; + this.datasets = _.map(o.datasets, initializeDataset); + function initializeDataset(oDataset) { + var node = that.$node.find(oDataset.node).first(); + oDataset.node = node.length ? node : $("
").appendTo(that.$node); + return new Dataset(oDataset, www); + } + } + _.mixin(Menu.prototype, EventEmitter, { + _onSelectableClick: function onSelectableClick($e) { + this.trigger("selectableClicked", $($e.currentTarget)); + }, + _onRendered: function onRendered(type, dataset, suggestions, async) { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetRendered", dataset, suggestions, async); + }, + _onCleared: function onCleared() { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetCleared"); + }, + _propagate: function propagate() { + this.trigger.apply(this, arguments); + }, + _allDatasetsEmpty: function allDatasetsEmpty() { + return _.every(this.datasets, _.bind(function isDatasetEmpty(dataset) { + var isEmpty = dataset.isEmpty(); + this.$node.attr("aria-expanded", !isEmpty); + return isEmpty; + }, this)); + }, + _getSelectables: function getSelectables() { + return this.$node.find(this.selectors.selectable); + }, + _removeCursor: function _removeCursor() { + var $selectable = this.getActiveSelectable(); + $selectable && $selectable.removeClass(this.classes.cursor); + }, + _ensureVisible: function ensureVisible($el) { + var elTop, elBottom, nodeScrollTop, nodeHeight; + elTop = $el.position().top; + elBottom = elTop + $el.outerHeight(true); + nodeScrollTop = this.$node.scrollTop(); + nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10); + if (elTop < 0) { + this.$node.scrollTop(nodeScrollTop + elTop); + } else if (nodeHeight < elBottom) { + this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight)); + } + }, + bind: function() { + var that = this, onSelectableClick; + onSelectableClick = _.bind(this._onSelectableClick, this); + this.$node.on("click.tt", this.selectors.selectable, onSelectableClick); + this.$node.on("mouseover", this.selectors.selectable, function() { + that.setCursor($(this)); + }); + this.$node.on("mouseleave", function() { + that._removeCursor(); + }); + _.each(this.datasets, function(dataset) { + dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that); + }); + return this; + }, + isOpen: function isOpen() { + return this.$node.hasClass(this.classes.open); + }, + open: function open() { + this.$node.scrollTop(0); + this.$node.addClass(this.classes.open); + }, + close: function close() { + this.$node.attr("aria-expanded", false); + this.$node.removeClass(this.classes.open); + this._removeCursor(); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.attr("dir", dir); + }, + selectableRelativeToCursor: function selectableRelativeToCursor(delta) { + var $selectables, $oldCursor, oldIndex, newIndex; + $oldCursor = this.getActiveSelectable(); + $selectables = this._getSelectables(); + oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1; + newIndex = oldIndex + delta; + newIndex = (newIndex + 1) % ($selectables.length + 1) - 1; + newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex; + return newIndex === -1 ? null : $selectables.eq(newIndex); + }, + setCursor: function setCursor($selectable) { + this._removeCursor(); + if ($selectable = $selectable && $selectable.first()) { + $selectable.addClass(this.classes.cursor); + this._ensureVisible($selectable); + } + }, + getSelectableData: function getSelectableData($el) { + return $el && $el.length ? Dataset.extractData($el) : null; + }, + getActiveSelectable: function getActiveSelectable() { + var $selectable = this._getSelectables().filter(this.selectors.cursor).first(); + return $selectable.length ? $selectable : null; + }, + getTopSelectable: function getTopSelectable() { + var $selectable = this._getSelectables().first(); + return $selectable.length ? $selectable : null; + }, + update: function update(query) { + var isValidUpdate = query !== this.query; + if (isValidUpdate) { + this.query = query; + _.each(this.datasets, updateDataset); + } + return isValidUpdate; + function updateDataset(dataset) { + dataset.update(query); + } + }, + empty: function empty() { + _.each(this.datasets, clearDataset); + this.query = null; + this.$node.addClass(this.classes.empty); + function clearDataset(dataset) { + dataset.clear(); + } + }, + destroy: function destroy() { + this.$node.off(".tt"); + this.$node = $("
"); + _.each(this.datasets, destroyDataset); + function destroyDataset(dataset) { + dataset.destroy(); + } + } + }); + return Menu; + }(); + var Status = function() { + "use strict"; + function Status(options) { + this.$el = $("", { + role: "status", + "aria-live": "polite" + }).css({ + position: "absolute", + padding: "0", + border: "0", + height: "1px", + width: "1px", + "margin-bottom": "-1px", + "margin-right": "-1px", + overflow: "hidden", + clip: "rect(0 0 0 0)", + "white-space": "nowrap" + }); + options.$input.after(this.$el); + _.each(options.menu.datasets, _.bind(function(dataset) { + if (dataset.onSync) { + dataset.onSync("rendered", _.bind(this.update, this)); + dataset.onSync("cleared", _.bind(this.cleared, this)); + } + }, this)); + } + _.mixin(Status.prototype, { + update: function update(event, suggestions) { + var length = suggestions.length; + var words; + if (length === 1) { + words = { + result: "result", + is: "is" + }; + } else { + words = { + result: "results", + is: "are" + }; + } + this.$el.text(length + " " + words.result + " " + words.is + " available, use up and down arrow keys to navigate."); + }, + cleared: function() { + this.$el.text(""); + } + }); + return Status; + }(); + var DefaultMenu = function() { + "use strict"; + var s = Menu.prototype; + function DefaultMenu() { + Menu.apply(this, [].slice.call(arguments, 0)); + } + _.mixin(DefaultMenu.prototype, Menu.prototype, { + open: function open() { + !this._allDatasetsEmpty() && this._show(); + return s.open.apply(this, [].slice.call(arguments, 0)); + }, + close: function close() { + this._hide(); + return s.close.apply(this, [].slice.call(arguments, 0)); + }, + _onRendered: function onRendered() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onRendered.apply(this, [].slice.call(arguments, 0)); + }, + _onCleared: function onCleared() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onCleared.apply(this, [].slice.call(arguments, 0)); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl); + return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0)); + }, + _hide: function hide() { + this.$node.hide(); + }, + _show: function show() { + this.$node.css("display", "block"); + } + }); + return DefaultMenu; + }(); + var Typeahead = function() { + "use strict"; + function Typeahead(o, www) { + var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged; + o = o || {}; + if (!o.input) { + $.error("missing input"); + } + if (!o.menu) { + $.error("missing menu"); + } + if (!o.eventBus) { + $.error("missing event bus"); + } + www.mixin(this); + this.eventBus = o.eventBus; + this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; + this.input = o.input; + this.menu = o.menu; + this.enabled = true; + this.autoselect = !!o.autoselect; + this.active = false; + this.input.hasFocus() && this.activate(); + this.dir = this.input.getLangDir(); + this._hacks(); + this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this); + onFocused = c(this, "activate", "open", "_onFocused"); + onBlurred = c(this, "deactivate", "_onBlurred"); + onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed"); + onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed"); + onEscKeyed = c(this, "isActive", "_onEscKeyed"); + onUpKeyed = c(this, "isActive", "open", "_onUpKeyed"); + onDownKeyed = c(this, "isActive", "open", "_onDownKeyed"); + onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed"); + onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed"); + onQueryChanged = c(this, "_openIfActive", "_onQueryChanged"); + onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged"); + this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this); + } + _.mixin(Typeahead.prototype, { + _hacks: function hacks() { + var $input, $menu; + $input = this.input.$input || $("
"); + $menu = this.menu.$node || $("
"); + $input.on("blur.tt", function($e) { + var active, isActive, hasActive; + active = document.activeElement; + isActive = $menu.is(active); + hasActive = $menu.has(active).length > 0; + if (_.isMsie() && (isActive || hasActive)) { + $e.preventDefault(); + $e.stopImmediatePropagation(); + _.defer(function() { + $input.focus(); + }); + } + }); + $menu.on("mousedown.tt", function($e) { + $e.preventDefault(); + }); + }, + _onSelectableClicked: function onSelectableClicked(type, $el) { + this.select($el); + }, + _onDatasetCleared: function onDatasetCleared() { + this._updateHint(); + }, + _onDatasetRendered: function onDatasetRendered(type, suggestions, async, dataset) { + this._updateHint(); + if (this.autoselect) { + var cursorClass = this.selectors.cursor.substr(1); + this.menu.$node.find(this.selectors.suggestion).first().addClass(cursorClass); + } + this.eventBus.trigger("render", suggestions, async, dataset); + }, + _onAsyncRequested: function onAsyncRequested(type, dataset, query) { + this.eventBus.trigger("asyncrequest", query, dataset); + }, + _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) { + this.eventBus.trigger("asynccancel", query, dataset); + }, + _onAsyncReceived: function onAsyncReceived(type, dataset, query) { + this.eventBus.trigger("asyncreceive", query, dataset); + }, + _onFocused: function onFocused() { + this._minLengthMet() && this.menu.update(this.input.getQuery()); + }, + _onBlurred: function onBlurred() { + if (this.input.hasQueryChangedSinceLastFocus()) { + this.eventBus.trigger("change", this.input.getQuery()); + } + }, + _onEnterKeyed: function onEnterKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + if (this.select($selectable)) { + $e.preventDefault(); + $e.stopPropagation(); + } + } else if (this.autoselect) { + if (this.select(this.menu.getTopSelectable())) { + $e.preventDefault(); + $e.stopPropagation(); + } + } + }, + _onTabKeyed: function onTabKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + this.select($selectable) && $e.preventDefault(); + } else if (this.autoselect) { + if ($selectable = this.menu.getTopSelectable()) { + this.autocomplete($selectable) && $e.preventDefault(); + } + } + }, + _onEscKeyed: function onEscKeyed() { + this.close(); + }, + _onUpKeyed: function onUpKeyed() { + this.moveCursor(-1); + }, + _onDownKeyed: function onDownKeyed() { + this.moveCursor(+1); + }, + _onLeftKeyed: function onLeftKeyed() { + if (this.dir === "rtl" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); + } + }, + _onRightKeyed: function onRightKeyed() { + if (this.dir === "ltr" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); + } + }, + _onQueryChanged: function onQueryChanged(e, query) { + this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty(); + }, + _onWhitespaceChanged: function onWhitespaceChanged() { + this._updateHint(); + }, + _onLangDirChanged: function onLangDirChanged(e, dir) { + if (this.dir !== dir) { + this.dir = dir; + this.menu.setLanguageDirection(dir); + } + }, + _openIfActive: function openIfActive() { + this.isActive() && this.open(); + }, + _minLengthMet: function minLengthMet(query) { + query = _.isString(query) ? query : this.input.getQuery() || ""; + return query.length >= this.minLength; + }, + _updateHint: function updateHint() { + var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match; + $selectable = this.menu.getTopSelectable(); + data = this.menu.getSelectableData($selectable); + val = this.input.getInputValue(); + if (data && !_.isBlankString(val) && !this.input.hasOverflow()) { + query = Input.normalizeQuery(val); + escapedQuery = _.escapeRegExChars(query); + frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i"); + match = frontMatchRegEx.exec(data.val); + match && this.input.setHint(val + match[1]); + } else { + this.input.clearHint(); + } + }, + isEnabled: function isEnabled() { + return this.enabled; + }, + enable: function enable() { + this.enabled = true; + }, + disable: function disable() { + this.enabled = false; + }, + isActive: function isActive() { + return this.active; + }, + activate: function activate() { + if (this.isActive()) { + return true; + } else if (!this.isEnabled() || this.eventBus.before("active")) { + return false; + } else { + this.active = true; + this.eventBus.trigger("active"); + return true; + } + }, + deactivate: function deactivate() { + if (!this.isActive()) { + return true; + } else if (this.eventBus.before("idle")) { + return false; + } else { + this.active = false; + this.close(); + this.eventBus.trigger("idle"); + return true; + } + }, + isOpen: function isOpen() { + return this.menu.isOpen(); + }, + open: function open() { + if (!this.isOpen() && !this.eventBus.before("open")) { + this.input.setAriaExpanded(true); + this.menu.open(); + this._updateHint(); + this.eventBus.trigger("open"); + } + return this.isOpen(); + }, + close: function close() { + if (this.isOpen() && !this.eventBus.before("close")) { + this.input.setAriaExpanded(false); + this.menu.close(); + this.input.clearHint(); + this.input.resetInputValue(); + this.eventBus.trigger("close"); + } + return !this.isOpen(); + }, + setVal: function setVal(val) { + this.input.setQuery(_.toStr(val)); + }, + getVal: function getVal() { + return this.input.getQuery(); + }, + select: function select($selectable) { + var data = this.menu.getSelectableData($selectable); + if (data && !this.eventBus.before("select", data.obj, data.dataset)) { + this.input.setQuery(data.val, true); + this.eventBus.trigger("select", data.obj, data.dataset); + this.close(); + return true; + } + return false; + }, + autocomplete: function autocomplete($selectable) { + var query, data, isValid; + query = this.input.getQuery(); + data = this.menu.getSelectableData($selectable); + isValid = data && query !== data.val; + if (isValid && !this.eventBus.before("autocomplete", data.obj, data.dataset)) { + this.input.setQuery(data.val); + this.eventBus.trigger("autocomplete", data.obj, data.dataset); + return true; + } + return false; + }, + moveCursor: function moveCursor(delta) { + var query, $candidate, data, suggestion, datasetName, cancelMove, id; + query = this.input.getQuery(); + $candidate = this.menu.selectableRelativeToCursor(delta); + data = this.menu.getSelectableData($candidate); + suggestion = data ? data.obj : null; + datasetName = data ? data.dataset : null; + id = $candidate ? $candidate.attr("id") : null; + this.input.trigger("cursorchange", id); + cancelMove = this._minLengthMet() && this.menu.update(query); + if (!cancelMove && !this.eventBus.before("cursorchange", suggestion, datasetName)) { + this.menu.setCursor($candidate); + if (data) { + if (typeof data.val === "string") { + this.input.setInputValue(data.val); + } + } else { + this.input.resetInputValue(); + this._updateHint(); + } + this.eventBus.trigger("cursorchange", suggestion, datasetName); + return true; + } + return false; + }, + destroy: function destroy() { + this.input.destroy(); + this.menu.destroy(); + } + }); + return Typeahead; + function c(ctx) { + var methods = [].slice.call(arguments, 1); + return function() { + var args = [].slice.call(arguments); + _.each(methods, function(method) { + return ctx[method].apply(ctx, args); + }); + }; + } + }(); + (function() { + "use strict"; + var old, keys, methods; + old = $.fn.typeahead; + keys = { + www: "tt-www", + attrs: "tt-attrs", + typeahead: "tt-typeahead" + }; + methods = { + initialize: function initialize(o, datasets) { + var www; + datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); + o = o || {}; + www = WWW(o.classNames); + return this.each(attach); + function attach() { + var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, status, typeahead, MenuConstructor; + _.each(datasets, function(d) { + d.highlight = !!o.highlight; + }); + $input = $(this); + $wrapper = $(www.html.wrapper); + $hint = $elOrNull(o.hint); + $menu = $elOrNull(o.menu); + defaultHint = o.hint !== false && !$hint; + defaultMenu = o.menu !== false && !$menu; + defaultHint && ($hint = buildHintFromInput($input, www)); + defaultMenu && ($menu = $(www.html.menu).css(www.css.menu)); + $hint && $hint.val(""); + $input = prepInput($input, www); + if (defaultHint || defaultMenu) { + $wrapper.css(www.css.wrapper); + $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint); + $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null); + } + MenuConstructor = defaultMenu ? DefaultMenu : Menu; + eventBus = new EventBus({ + el: $input + }); + input = new Input({ + hint: $hint, + input: $input, + menu: $menu + }, www); + menu = new MenuConstructor({ + node: $menu, + datasets: datasets + }, www); + status = new Status({ + $input: $input, + menu: menu + }); + typeahead = new Typeahead({ + input: input, + menu: menu, + eventBus: eventBus, + minLength: o.minLength, + autoselect: o.autoselect + }, www); + $input.data(keys.www, www); + $input.data(keys.typeahead, typeahead); + } + }, + isEnabled: function isEnabled() { + var enabled; + ttEach(this.first(), function(t) { + enabled = t.isEnabled(); + }); + return enabled; + }, + enable: function enable() { + ttEach(this, function(t) { + t.enable(); + }); + return this; + }, + disable: function disable() { + ttEach(this, function(t) { + t.disable(); + }); + return this; + }, + isActive: function isActive() { + var active; + ttEach(this.first(), function(t) { + active = t.isActive(); + }); + return active; + }, + activate: function activate() { + ttEach(this, function(t) { + t.activate(); + }); + return this; + }, + deactivate: function deactivate() { + ttEach(this, function(t) { + t.deactivate(); + }); + return this; + }, + isOpen: function isOpen() { + var open; + ttEach(this.first(), function(t) { + open = t.isOpen(); + }); + return open; + }, + open: function open() { + ttEach(this, function(t) { + t.open(); + }); + return this; + }, + close: function close() { + ttEach(this, function(t) { + t.close(); + }); + return this; + }, + select: function select(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.select($el); + }); + return success; + }, + autocomplete: function autocomplete(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.autocomplete($el); + }); + return success; + }, + moveCursor: function moveCursoe(delta) { + var success = false; + ttEach(this.first(), function(t) { + success = t.moveCursor(delta); + }); + return success; + }, + val: function val(newVal) { + var query; + if (!arguments.length) { + ttEach(this.first(), function(t) { + query = t.getVal(); + }); + return query; + } else { + ttEach(this, function(t) { + t.setVal(_.toStr(newVal)); + }); + return this; + } + }, + destroy: function destroy() { + ttEach(this, function(typeahead, $input) { + revert($input); + typeahead.destroy(); + }); + return this; + } + }; + $.fn.typeahead = function(method) { + if (methods[method]) { + return methods[method].apply(this, [].slice.call(arguments, 1)); + } else { + return methods.initialize.apply(this, arguments); + } + }; + $.fn.typeahead.noConflict = function noConflict() { + $.fn.typeahead = old; + return this; + }; + function ttEach($els, fn) { + $els.each(function() { + var $input = $(this), typeahead; + (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input); + }); + } + function buildHintFromInput($input, www) { + return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop({ + readonly: true, + required: false + }).removeAttr("id name placeholder").removeClass("required").attr({ + spellcheck: "false", + tabindex: -1 + }); + } + function prepInput($input, www) { + $input.data(keys.attrs, { + dir: $input.attr("dir"), + autocomplete: $input.attr("autocomplete"), + spellcheck: $input.attr("spellcheck"), + style: $input.attr("style") + }); + $input.addClass(www.classes.input).attr({ + spellcheck: false + }); + try { + !$input.attr("dir") && $input.attr("dir", "auto"); + } catch (e) {} + return $input; + } + function getBackgroundStyles($el) { + return { + backgroundAttachment: $el.css("background-attachment"), + backgroundClip: $el.css("background-clip"), + backgroundColor: $el.css("background-color"), + backgroundImage: $el.css("background-image"), + backgroundOrigin: $el.css("background-origin"), + backgroundPosition: $el.css("background-position"), + backgroundRepeat: $el.css("background-repeat"), + backgroundSize: $el.css("background-size") + }; + } + function revert($input) { + var www, $wrapper; + www = $input.data(keys.www); + $wrapper = $input.parent().filter(www.selectors.wrapper); + _.each($input.data(keys.attrs), function(val, key) { + _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); + }); + $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input); + if ($wrapper.length) { + $input.detach().insertAfter($wrapper); + $wrapper.remove(); + } + } + function $elOrNull(obj) { + var isValid, $el; + isValid = _.isJQuery(obj) || _.isElement(obj); + $el = isValid ? $(obj).first() : []; + return $el.length ? $el : null; + } + })(); +}); \ No newline at end of file diff --git a/docs/1.5.0/AsyncHTTPClient/search.json b/docs/1.5.0/AsyncHTTPClient/search.json new file mode 100644 index 000000000..fe97234c3 --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/search.json @@ -0,0 +1 @@ +{"Structs/HTTPClientError.html#/s:s23CustomStringConvertibleP11descriptionSSvp":{"name":"description","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV10invalidURLACvpZ":{"name":"invalidURL","abstract":"

URL provided is invalid.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV9emptyHostACvpZ":{"name":"emptyHost","abstract":"

URL does not contain host.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV17missingSocketPathACvpZ":{"name":"missingSocketPath","abstract":"

URL does not contain a socketPath as a host for http(s)+unix shemes.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV15alreadyShutdownACvpZ":{"name":"alreadyShutdown","abstract":"

Client is shutdown and cannot be used for new requests.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV11emptySchemeACvpZ":{"name":"emptyScheme","abstract":"

URL does not contain scheme.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV17unsupportedSchemeyACSSFZ":{"name":"unsupportedScheme(_:)","abstract":"

Provided URL scheme is not supported, supported schemes are: http and https

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV11readTimeoutACvpZ":{"name":"readTimeout","abstract":"

Request timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV22remoteConnectionClosedACvpZ":{"name":"remoteConnectionClosed","abstract":"

Remote connection was closed unexpectedly.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV9cancelledACvpZ":{"name":"cancelled","abstract":"

Request was cancelled.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV32identityCodingIncorrectlyPresentACvpZ":{"name":"identityCodingIncorrectlyPresent","abstract":"

Request contains invalid identity encoding.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV29chunkedSpecifiedMultipleTimesACvpZ":{"name":"chunkedSpecifiedMultipleTimes","abstract":"

Request contains multiple chunks definitions.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20invalidProxyResponseACvpZ":{"name":"invalidProxyResponse","abstract":"

Proxy response was invalid.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20contentLengthMissingACvpZ":{"name":"contentLengthMissing","abstract":"

Request does not contain Content-Length header.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV27proxyAuthenticationRequiredACvpZ":{"name":"proxyAuthenticationRequired","abstract":"

Proxy Authentication Required.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20redirectLimitReachedACvpZ":{"name":"redirectLimitReached","abstract":"

Redirect Limit reached.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV21redirectCycleDetectedACvpZ":{"name":"redirectCycleDetected","abstract":"

Redirect Cycle detected.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV15uncleanShutdownACvpZ":{"name":"uncleanShutdown","abstract":"

Unclean shutdown.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20traceRequestWithBodyACvpZ":{"name":"traceRequestWithBody","abstract":"

A body was sent in a request with method TRACE.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV23invalidHeaderFieldNamesyACSaySSGFZ":{"name":"invalidHeaderFieldNames(_:)","abstract":"

Header field names contain invalid characters.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV18bodyLengthMismatchACvpZ":{"name":"bodyLengthMismatch","abstract":"

Body length is not equal to Content-Length.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV21writeAfterRequestSentACvpZ":{"name":"writeAfterRequestSent","abstract":"

Body part was written after request was fully sent.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV19incompatibleHeadersACvpZ":{"name":"incompatibleHeaders","abstract":"

Incompatible headers specified, for example Transfer-Encoding and Content-Length.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV14connectTimeoutACvpZ":{"name":"connectTimeout","abstract":"

Creating a new tcp connection timed out

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV21socksHandshakeTimeoutACvpZ":{"name":"socksHandshakeTimeout","abstract":"

The socks handshake timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV25httpProxyHandshakeTimeoutACvpZ":{"name":"httpProxyHandshakeTimeout","abstract":"

The http proxy connection creation timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV19tlsHandshakeTimeoutACvpZ":{"name":"tlsHandshakeTimeout","abstract":"

The tls handshake timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV43serverOfferedUnsupportedApplicationProtocolyACSSFZ":{"name":"serverOfferedUnsupportedApplicationProtocol(_:)","abstract":"

The remote server only offered an unsupported application protocol

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html":{"name":"HTTPClientError","abstract":"

Possible client errors.

"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP0C0Qa":{"name":"Response","abstract":"

Undocumented

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didSendRequestHead4task_yAA0B0C4TaskCy_0C0QzG_8NIOHTTP1011HTTPRequestH0VtF":{"name":"didSendRequestHead(task:_:)","abstract":"

Called when the request head is sent. Will be called once.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didSendRequestPart4task_yAA0B0C4TaskCy_0C0QzG_7NIOCore6IODataOtF":{"name":"didSendRequestPart(task:_:)","abstract":"

Called when a part of the request body is sent. Could be called zero or more times.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didSendRequest4taskyAA0B0C4TaskCy_0C0QzG_tF":{"name":"didSendRequest(task:)","abstract":"

Called when the request is fully sent. Will be called once.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","abstract":"

Called when response head is received. Will be called once.","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","abstract":"

Called when part of a response body is received. Could be called zero or more times.","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","abstract":"

Called when error was thrown during request execution. Will be called zero or one time only. Request processing will be stopped after that.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","abstract":"

Called when the complete HTTP request is finished. You must return an instance of your Response associated type. Will be called once, except if an error occurred.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html":{"name":"HTTPClientResponseDelegate","abstract":"

HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed."},"Extensions/URL.html#/s:10Foundation3URLV15AsyncHTTPClientE21httpURLWithSocketPath3uriACSgSS_SStcfc":{"name":"init(httpURLWithSocketPath:uri:)","abstract":"

Initializes a newly created HTTP URL connecting to a unix domain socket path. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “http+unix” scheme.

","parent_name":"URL"},"Extensions/URL.html#/s:10Foundation3URLV15AsyncHTTPClientE22httpsURLWithSocketPath3uriACSgSS_SStcfc":{"name":"init(httpsURLWithSocketPath:uri:)","abstract":"

Initializes a newly created HTTPS URL connecting to a unix domain socket path over TLS. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “https+unix” scheme.

","parent_name":"URL"},"Extensions/URL.html":{"name":"URL"},"Extensions.html#/HTTP1ConnectionProvider":{"name":"HTTP1ConnectionProvider"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B15CopyingDelegateC8Responsea":{"name":"Response","abstract":"

Undocumented

","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B15CopyingDelegateC12chunkHandlerAC7NIOCore15EventLoopFutureCyytGAE10ByteBufferVc_tcfc":{"name":"init(chunkHandler:)","abstract":"

Undocumented

","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"HTTPClientCopyingDelegate"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient19ResponseAccumulatorC0C0a":{"name":"Response","abstract":"

Undocumented

","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient19ResponseAccumulatorC7requestAcA0B0C7RequestV_tcfc":{"name":"init(request:)","abstract":"

Undocumented

","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"ResponseAccumulator"},"Classes/HTTPClient/NWTLSError.html#/status":{"name":"status","abstract":"

TLS error status. List of TLS errors can be found in

","parent_name":"NWTLSError"},"Classes/HTTPClient/NWTLSError.html#/init(_:reason:)":{"name":"init(_:reason:)","abstract":"

initialise a NWTLSError

","parent_name":"NWTLSError"},"Classes/HTTPClient/NWTLSError.html#/description":{"name":"description","parent_name":"NWTLSError"},"Classes/HTTPClient/NWPOSIXError.html#/errorCode":{"name":"errorCode","abstract":"

POSIX error code (enum)

","parent_name":"NWPOSIXError"},"Classes/HTTPClient/NWPOSIXError.html#/init(_:reason:)":{"name":"init(_:reason:)","abstract":"

Initialise a NWPOSIXError

","parent_name":"NWPOSIXError"},"Classes/HTTPClient/NWPOSIXError.html#/description":{"name":"description","parent_name":"NWPOSIXError"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC9eventLoop7NIOCore05EventE0_pvp":{"name":"eventLoop","abstract":"

The EventLoop the delegate will be executed on.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC12futureResult7NIOCore15EventLoopFutureCyxGvp":{"name":"futureResult","abstract":"

EventLoopFuture for the response returned by this request.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC4waitxyKF":{"name":"wait()","abstract":"

Waits for execution of this request to complete.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC6cancelyyF":{"name":"cancel()","abstract":"

Cancels the request execution.

","parent_name":"Task"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV5basic8username8passwordAESS_SStFZ":{"name":"basic(username:password:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV5basic11credentialsAESS_tFZ":{"name":"basic(credentials:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV6bearer6tokensAESS_tFZ":{"name":"bearer(tokens:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV11headerValueSSvp":{"name":"headerValue","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6method8NIOHTTP110HTTPMethodOvp":{"name":"method","abstract":"

Request HTTP method, defaults to GET.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url10Foundation3URLVvp":{"name":"url","abstract":"

Remote URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6schemeSSvp":{"name":"scheme","abstract":"

Remote HTTP scheme, resolved from URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4hostSSvp":{"name":"host","abstract":"

Remote host, resolved from URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV7headers8NIOHTTP111HTTPHeadersVvp":{"name":"headers","abstract":"

Request custom HTTP Headers, defaults to no headers.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4bodyAC4BodyVSgvp":{"name":"body","abstract":"

Request body, defaults to no body.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV16tlsConfiguration6NIOSSL16TLSConfigurationVSgvp":{"name":"tlsConfiguration","abstract":"

Request-specific TLS configuration, defaults to no request-specific TLS configuration.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4bodyAESS_8NIOHTTP110HTTPMethodOAJ11HTTPHeadersVAC4BodyVSgtKcfc":{"name":"init(url:method:headers:body:)","abstract":"

Create HTTP request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4body16tlsConfigurationAESS_8NIOHTTP110HTTPMethodOAK11HTTPHeadersVAC4BodyVSg6NIOSSL16TLSConfigurationVSgtKcfc":{"name":"init(url:method:headers:body:tlsConfiguration:)","abstract":"

Create HTTP request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4bodyAE10Foundation3URLV_8NIOHTTP110HTTPMethodOAM11HTTPHeadersVAC4BodyVSgtKcfc":{"name":"init(url:method:headers:body:)","abstract":"

Create an HTTP Request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4body16tlsConfigurationAE10Foundation3URLV_8NIOHTTP110HTTPMethodOAN11HTTPHeadersVAC4BodyVSg6NIOSSL16TLSConfigurationVSgtKcfc":{"name":"init(url:method:headers:body:tlsConfiguration:)","abstract":"

Create an HTTP Request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6useTLSSbvp":{"name":"useTLS","abstract":"

Whether request will be executed using secure socket.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4portSivp":{"name":"port","abstract":"

Resolved port.

","parent_name":"Request"},"Classes/HTTPClient/Body/StreamWriter.html#/s:15AsyncHTTPClient0B0C4BodyV12StreamWriterV7closureAG7NIOCore15EventLoopFutureCyytGAI6IODataOc_tcfc":{"name":"init(closure:)","abstract":"

Create new StreamWriter

","parent_name":"StreamWriter"},"Classes/HTTPClient/Body/StreamWriter.html#/s:15AsyncHTTPClient0B0C4BodyV12StreamWriterV5writey7NIOCore15EventLoopFutureCyytGAI6IODataOF":{"name":"write(_:)","abstract":"

Write data to server.

","parent_name":"StreamWriter"},"Classes/HTTPClient/Body/StreamWriter.html":{"name":"StreamWriter","abstract":"

Chunk provider.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6lengthSiSgvp":{"name":"length","abstract":"

Body size. Request validation will be failed with HTTPClientErrors.contentLengthMissing if nil,","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6streamy7NIOCore15EventLoopFutureCyytGAE12StreamWriterVcvp":{"name":"stream","abstract":"

Body chunk provider.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV10byteBufferyAE7NIOCore04ByteE0VFZ":{"name":"byteBuffer(_:)","abstract":"

Create and stream body using ByteBuffer.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6stream6length_AESiSg_7NIOCore15EventLoopFutureCyytGAE12StreamWriterVctFZ":{"name":"stream(length:_:)","abstract":"

Create and stream body using StreamWriter.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV4datayAE10Foundation4DataVFZ":{"name":"data(_:)","abstract":"

Create and stream body using Data.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6stringyAESSFZ":{"name":"string(_:)","abstract":"

Create and stream body using String.

","parent_name":"Body"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4hostSSvp":{"name":"host","abstract":"

Remote host of the request.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV6status8NIOHTTP118HTTPResponseStatusOvp":{"name":"status","abstract":"

Response HTTP status.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7version8NIOHTTP111HTTPVersionVvp":{"name":"version","abstract":"

Response HTTP version.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7headers8NIOHTTP111HTTPHeadersVvp":{"name":"headers","abstract":"

Reponse HTTP headers.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4body7NIOCore10ByteBufferVSgvp":{"name":"body","abstract":"

Response body.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4host6status7headers4bodyAESS_8NIOHTTP118HTTPResponseStatusOAJ11HTTPHeadersV7NIOCore10ByteBufferVSgtcfc":{"name":"init(host:status:headers:body:)","abstract":"

Create HTTP Response.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4host6status7version7headers4bodyAESS_8NIOHTTP118HTTPResponseStatusOAK11HTTPVersionVAK11HTTPHeadersV7NIOCore10ByteBufferVSgtcfc":{"name":"init(host:status:version:headers:body:)","abstract":"

Create HTTP Response.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7cookiesSayAC6CookieVGvp":{"name":"cookies","abstract":"

List of HTTP cookies returned by the server.

","parent_name":"Response"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4nameSSvp":{"name":"name","abstract":"

The name of the cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV5valueSSvp":{"name":"value","abstract":"

The cookie’s string value.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4pathSSvp":{"name":"path","abstract":"

The cookie’s path.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6domainSSSgvp":{"name":"domain","abstract":"

The domain of the cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV7expires10Foundation4DateVSgvp":{"name":"expires","abstract":"

The cookie’s expiration date.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6maxAgeSiSgvp":{"name":"maxAge","abstract":"

The cookie’s age in seconds.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV8httpOnlySbvp":{"name":"httpOnly","abstract":"

Whether the cookie should only be sent to HTTP servers.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6secureSbvp":{"name":"secure","abstract":"

Whether the cookie should only be sent over secure channels.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6header13defaultDomainAESgSS_SStcfc":{"name":"init(header:defaultDomain:)","abstract":"

Create a Cookie by parsing a Set-Cookie header.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4name5value4path6domain7expires6maxAge8httpOnly6secureAESS_S3SSg10Foundation4DateVSgSiSgS2btcfc":{"name":"init(name:value:path:domain:expires:maxAge:httpOnly:secure:)","abstract":"

Create HTTP cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Decompression.html#/s:15AsyncHTTPClient0B0C13DecompressionO8disabledyA2EmF":{"name":"disabled","abstract":"

Decompression is disabled.

","parent_name":"Decompression"},"Classes/HTTPClient/Decompression.html#/s:15AsyncHTTPClient0B0C13DecompressionO7enabledyAE18NIOHTTPCompression20NIOHTTPDecompressionO0C5LimitV_tcAEmF":{"name":"enabled(limit:)","abstract":"

Decompression is enabled.

","parent_name":"Decompression"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV11indifferentAEvpZ":{"name":"indifferent","abstract":"

Event Loop will be selected by the library.

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV8delegate2onAE7NIOCore0cD0_p_tFZ":{"name":"delegate(on:)","abstract":"

The delegate will be run on the specified EventLoop (and the Channel if possible).

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV18delegateAndChannel2onAE7NIOCore0cD0_p_tFZ":{"name":"delegateAndChannel(on:)","abstract":"

The delegate and the Channel will be run on the specified EventLoop.

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:s23CustomStringConvertibleP11descriptionSSvp":{"name":"description","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopGroupProvider.html#/s:15AsyncHTTPClient0B0C22EventLoopGroupProviderO6sharedyAE7NIOCore0cdE0_pcAEmF":{"name":"shared(_:)","abstract":"

EventLoopGroup will be provided by the user. Owner of this group is responsible for its lifecycle.

","parent_name":"EventLoopGroupProvider"},"Classes/HTTPClient/EventLoopGroupProvider.html#/s:15AsyncHTTPClient0B0C22EventLoopGroupProviderO9createNewyA2EmF":{"name":"createNew","abstract":"

EventLoopGroup will be created by the client. When syncShutdown is called, created EventLoopGroup will be shut down as well.

","parent_name":"EventLoopGroupProvider"},"Classes/HTTPClient/Configuration/ConnectionPool.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14ConnectionPoolV11idleTimeout7NIOCore10TimeAmountVvp":{"name":"idleTimeout","abstract":"

Undocumented

","parent_name":"ConnectionPool"},"Classes/HTTPClient/Configuration/ConnectionPool.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14ConnectionPoolV11idleTimeoutAG7NIOCore10TimeAmountV_tcfc":{"name":"init(idleTimeout:)","abstract":"

Undocumented

","parent_name":"ConnectionPool"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08RedirectC0V8disallowAGvpZ":{"name":"disallow","abstract":"

Redirects are not followed.

","parent_name":"RedirectConfiguration"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08RedirectC0V6follow3max11allowCyclesAGSi_SbtFZ":{"name":"follow(max:allowCycles:)","abstract":"

Redirects are followed with a specified limit.

","parent_name":"RedirectConfiguration"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV7connect7NIOCore10TimeAmountVSgvp":{"name":"connect","abstract":"

Specifies connect timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV4read7NIOCore10TimeAmountVSgvp":{"name":"read","abstract":"

Specifies read timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV7connect4readAG7NIOCore10TimeAmountVSg_AMtcfc":{"name":"init(connect:read:)","abstract":"

Create timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV4hostSSvp":{"name":"host","abstract":"

Specifies Proxy server host.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV4portSivp":{"name":"port","abstract":"

Specifies Proxy server port.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV13authorizationAC13AuthorizationVSgvp":{"name":"authorization","abstract":"

Specifies Proxy server authorization.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV6server4host4portAGSS_SitFZ":{"name":"server(host:port:)","abstract":"

Create a HTTP proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV6server4host4port13authorizationAGSS_SiAC13AuthorizationVSgtFZ":{"name":"server(host:port:authorization:)","abstract":"

Create a HTTP proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV11socksServer4host4portAGSS_SitFZ":{"name":"socksServer(host:port:)","abstract":"

Create a SOCKSv5 proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC06NIOSSL16TLSConfigurationVSgvp":{"name":"tlsConfiguration","abstract":"

TLS configuration, defaults to TLSConfiguration.makeClientConfiguration().

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08redirectC0AE08RedirectC0Vvp":{"name":"redirectConfiguration","abstract":"

Enables following 3xx redirects automatically, defaults to RedirectConfiguration().

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7timeoutAE7TimeoutVvp":{"name":"timeout","abstract":"

Default client timeout, defaults to no read timeout and 10 seconds connect timeout.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14connectionPoolAE010ConnectionE0Vvp":{"name":"connectionPool","abstract":"

Connection pool configuration.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5proxyAE5ProxyVSgvp":{"name":"proxy","abstract":"

Upstream proxy, defaults to no proxy.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV13decompressionAC13DecompressionOvp":{"name":"decompression","abstract":"

Enables automatic body decompression. Supported algorithms are gzip and deflate.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV24ignoreUncleanSSLShutdownSbvp":{"name":"ignoreUncleanSSLShutdown","abstract":"

Ignore TLS unclean shutdown error, defaults to false.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC008redirectC07timeout14connectionPool5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL16TLSConfigurationVSg_AE08RedirectC0VSgAE7TimeoutVAE010ConnectionH0VAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(tlsConfiguration:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC008redirectC07timeout5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL16TLSConfigurationVSg_AE08RedirectC0VSgAE7TimeoutVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(tlsConfiguration:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout38maximumAllowedIdleTimeInConnectionPool5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutV7NIOCore0K6AmountVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:maximumAllowedIdleTimeInConnectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout14connectionPool5proxy24ignoreUncleanSSLShutdown13decompression24backgroundActivityLoggerAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutV7NIOCore10TimeAmountVAE5ProxyVSgSbAC13DecompressionO7Logging0Q0VSgtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:backgroundActivityLogger:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/Proxy.html":{"name":"Proxy","abstract":"

Proxy server configuration","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/Timeout.html":{"name":"Timeout","abstract":"

Timeout configuration.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html":{"name":"RedirectConfiguration","abstract":"

Specifies redirect processing settings.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/ConnectionPool.html":{"name":"ConnectionPool","abstract":"

Connection pool configuration.

","parent_name":"Configuration"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C14eventLoopGroup7NIOCore05EventdE0_pvp":{"name":"eventLoopGroup","abstract":"

Undocumented

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C22eventLoopGroupProvider13configurationA2C05EventdeF0O_AC13ConfigurationVtcfc":{"name":"init(eventLoopGroupProvider:configuration:)","abstract":"

Create an HTTPClient with specified EventLoopGroup provider and configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C22eventLoopGroupProvider13configuration24backgroundActivityLoggerA2C05EventdeF0O_AC13ConfigurationV7Logging0J0Vtcfc":{"name":"init(eventLoopGroupProvider:configuration:backgroundActivityLogger:)","abstract":"

Create an HTTPClient with specified EventLoopGroup provider and configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C12syncShutdownyyKF":{"name":"syncShutdown()","abstract":"

Shuts down the client and EventLoopGroup if it was created by the client.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C8shutdown5queue_y8Dispatch0E5QueueC_ys5Error_pSgctF":{"name":"shutdown(queue:_:)","abstract":"

Shuts down the client and event loop gracefully. This function is clearly an outlier in that it uses a completion","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3get3url8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AG11NIODeadlineVSgtF":{"name":"get(url:deadline:)","abstract":"

Execute GET request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3get3url8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"get(url:deadline:logger:)","abstract":"

Execute GET request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C4post3url4body8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"post(url:body:deadline:)","abstract":"

Execute POST request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C4post3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"post(url:body:deadline:logger:)","abstract":"

Execute POST request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C5patch3url4body8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"patch(url:body:deadline:)","abstract":"

Execute PATCH request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C5patch3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"patch(url:body:deadline:logger:)","abstract":"

Execute PATCH request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3put3url4body8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"put(url:body:deadline:)","abstract":"

Execute PUT request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3put3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"put(url:body:deadline:logger:)","abstract":"

Execute PUT request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C6delete3url8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AG11NIODeadlineVSgtF":{"name":"delete(url:deadline:)","abstract":"

Execute DELETE request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C6delete3url8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"delete(url:deadline:logger:)","abstract":"

Execute DELETE request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_SSAC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:url:body:deadline:logger:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_10socketPath03urlE04body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_S2SAC4BodyVSgAJ11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:socketPath:urlPath:body:deadline:logger:)","abstract":"

Execute arbitrary HTTP+UNIX request to a unix domain socket path, using the specified URL as the request to send to the server.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_16secureSocketPath03urlF04body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_S2SAC4BodyVSgAJ11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:secureSocketPath:urlPath:body:deadline:logger:)","abstract":"

Execute arbitrary HTTPS+UNIX request to a unix domain socket path over TLS, using the specified URL as the request to send to the server.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGAC7RequestV_AG11NIODeadlineVSgtF":{"name":"execute(request:deadline:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGAC7RequestV_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"execute(request:deadline:logger:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request9eventLoop8deadline7NIOCore05EventF6FutureCyAC8ResponseVGAC7RequestV_AC0iF10PreferenceVAH11NIODeadlineVSgtF":{"name":"execute(request:eventLoop:deadline:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request9eventLoop8deadline6logger7NIOCore05EventF6FutureCyAC8ResponseVGAC7RequestV_AC0jF10PreferenceVAI11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(request:eventLoop:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate8deadlineAC4TaskCy_8ResponseQzGAC7RequestV_x7NIOCore11NIODeadlineVSgtAA0bH8DelegateRzlF":{"name":"execute(request:delegate:deadline:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate8deadline6loggerAC4TaskCy_8ResponseQzGAC7RequestV_x7NIOCore11NIODeadlineVSg7Logging6LoggerVtAA0bI8DelegateRzlF":{"name":"execute(request:delegate:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate9eventLoop8deadlineAC4TaskCy_8ResponseQzGAC7RequestV_xAC05EventG10PreferenceV7NIOCore11NIODeadlineVSgtAA0bJ8DelegateRzlF":{"name":"execute(request:delegate:eventLoop:deadline:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate9eventLoop8deadline6loggerAC4TaskCy_8ResponseQzGAC7RequestV_xAC05EventG10PreferenceV7NIOCore11NIODeadlineVSg7Logging6LoggerVSgtAA0bK8DelegateRzlF":{"name":"execute(request:delegate:eventLoop:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Configuration.html":{"name":"Configuration","abstract":"

HTTPClient configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient/EventLoopGroupProvider.html":{"name":"EventLoopGroupProvider","abstract":"

Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

","parent_name":"HTTPClient"},"Classes/HTTPClient/EventLoopPreference.html":{"name":"EventLoopPreference","abstract":"

Specifies how the library will treat event loop passed by the user.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Decompression.html":{"name":"Decompression","abstract":"

Specifies decompression settings.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Cookie.html":{"name":"Cookie","abstract":"

A representation of an HTTP cookie.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Response.html":{"name":"Response","abstract":"

Represent HTTP response.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Body.html":{"name":"Body","abstract":"

Represent request body.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Request.html":{"name":"Request","abstract":"

Represent HTTP request.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Authorization.html":{"name":"Authorization","abstract":"

HTTP authentication

","parent_name":"HTTPClient"},"Classes/HTTPClient/Task.html":{"name":"Task","abstract":"

Response execution context. Will be created by the library and could be used for obtaining","parent_name":"HTTPClient"},"Classes/HTTPClient/NWPOSIXError.html":{"name":"NWPOSIXError","parent_name":"HTTPClient"},"Classes/HTTPClient/NWTLSError.html":{"name":"NWTLSError","parent_name":"HTTPClient"},"Classes/FileDownloadDelegate/Progress.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8ProgressV10totalBytesSiSgvp":{"name":"totalBytes","abstract":"

Undocumented

","parent_name":"Progress"},"Classes/FileDownloadDelegate/Progress.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8ProgressV13receivedBytesSivp":{"name":"receivedBytes","abstract":"

Undocumented

","parent_name":"Progress"},"Classes/FileDownloadDelegate/Progress.html":{"name":"Progress","abstract":"

The response type for this delegate: the total count of bytes as reported by the response","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8Responsea":{"name":"Response","abstract":"

Undocumented

","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient20FileDownloadDelegateC4path4pool10reportHead0H8ProgressACSS_8NIOPosix13NIOThreadPoolCy8NIOHTTP1012HTTPResponseI0VcSgyAC0J0VcSgtKcfc":{"name":"init(path:pool:reportHead:reportProgress:)","abstract":"

Initializes a new file download delegate.

","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html":{"name":"FileDownloadDelegate","abstract":"

Handles a streaming download to a given file path, allowing headers and progress to be reported.

"},"Classes/HTTPClient.html":{"name":"HTTPClient","abstract":"

HTTPClient class provides API for request execution.

"},"Classes/ResponseAccumulator.html":{"name":"ResponseAccumulator","abstract":"

Undocumented

"},"Classes/HTTPClientCopyingDelegate.html":{"name":"HTTPClientCopyingDelegate","abstract":"

Undocumented

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Extensions.html":{"name":"Extensions","abstract":"

The following extensions are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"},"Structs.html":{"name":"Structures","abstract":"

The following structures are available globally.

"}} \ No newline at end of file diff --git a/docs/1.5.0/AsyncHTTPClient/undocumented.json b/docs/1.5.0/AsyncHTTPClient/undocumented.json new file mode 100644 index 000000000..2fd81ab68 --- /dev/null +++ b/docs/1.5.0/AsyncHTTPClient/undocumented.json @@ -0,0 +1,159 @@ +{ + "warnings": [ + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 23, + "symbol": "FileDownloadDelegate.Progress.totalBytes", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 24, + "symbol": "FileDownloadDelegate.Progress.receivedBytes", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 29, + "symbol": "FileDownloadDelegate.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 67, + "symbol": "HTTPClient.eventLoopGroup", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 664, + "symbol": "HTTPClient.Configuration.init(tlsConfiguration:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 680, + "symbol": "HTTPClient.Configuration.init(tlsConfiguration:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 697, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:maximumAllowedIdleTimeInConnectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 715, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:backgroundActivityLogger:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 734, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 888, + "symbol": "HTTPClient.Configuration.ConnectionPool.idleTimeout", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 890, + "symbol": "HTTPClient.Configuration.ConnectionPool.init(idleTimeout:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 357, + "symbol": "HTTPClient.Authorization.basic(username:password:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 361, + "symbol": "HTTPClient.Authorization.basic(credentials:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 365, + "symbol": "HTTPClient.Authorization.bearer(tokens:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 369, + "symbol": "HTTPClient.Authorization.headerValue", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 380, + "symbol": "ResponseAccumulator", + "symbol_kind": "source.lang.swift.decl.class", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 381, + "symbol": "ResponseAccumulator.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 394, + "symbol": "ResponseAccumulator.init(request:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 487, + "symbol": "HTTPClientResponseDelegate.Response", + "symbol_kind": "source.lang.swift.decl.associatedtype", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 26, + "symbol": "HTTPClientCopyingDelegate", + "symbol_kind": "source.lang.swift.decl.class", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 27, + "symbol": "HTTPClientCopyingDelegate.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 31, + "symbol": "HTTPClientCopyingDelegate.init(chunkHandler:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + } + ], + "source_directory": "/code" +} \ No newline at end of file diff --git a/docs/1.5.1/AsyncHTTPClient/Classes.html b/docs/1.5.1/AsyncHTTPClient/Classes.html new file mode 100644 index 000000000..6c5e0c86d --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes.html @@ -0,0 +1,315 @@ + + + + Classes Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Classes

+

The following classes are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + FileDownloadDelegate + +
    +
    +
    +
    +
    +
    +

    Handles a streaming download to a given file path, allowing headers and progress to be reported.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class FileDownloadDelegate : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + HTTPClient + +
    +
    +
    +
    +
    +
    +

    HTTPClient class provides API for request execution.

    + +

    Example:

    +
        let client = HTTPClient(eventLoopGroupProvider: .createNew)
    +    client.get(url: "https://swift.org/", deadline: .now() + .seconds(1)).whenComplete { result in
    +        switch result {
    +        case .failure(let error):
    +            // process error
    +        case .success(let response):
    +            if let response.status == .ok {
    +                // handle response
    +            } else {
    +                // handle remote error
    +            }
    +        }
    +    }
    +
    + +

    It is important to close the client instance, for example in a defer statement, after use to cleanly shutdown the underlying NIO EventLoopGroup:

    +
        try client.syncShutdown()
    +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class HTTPClient
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + ResponseAccumulator + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class ResponseAccumulator : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Undocumented

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class HTTPClientCopyingDelegate : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.5.1/AsyncHTTPClient/Classes/FileDownloadDelegate.html b/docs/1.5.1/AsyncHTTPClient/Classes/FileDownloadDelegate.html new file mode 100644 index 000000000..9a50c5c95 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes/FileDownloadDelegate.html @@ -0,0 +1,458 @@ + + + + FileDownloadDelegate Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

FileDownloadDelegate

+
+
+ +
public final class FileDownloadDelegate : HTTPClientResponseDelegate
+ +
+
+

Handles a streaming download to a given file path, allowing headers and progress to be reported.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + Progress + +
    +
    +
    +
    +
    +
    +

    The response type for this delegate: the total count of bytes as reported by the response +“Content-Length” header (if available) and the count of bytes downloaded.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Progress
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public typealias Response = Progress
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Initializes a new file download delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(
    +    path: String,
    +    pool: NIOThreadPool = NIOThreadPool(numberOfThreads: 1),
    +    reportHead: ((HTTPResponseHead) -> Void)? = nil,
    +    reportProgress: ((Progress) -> Void)? = nil
    +) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + path + + +
    +

    Path to a file you’d like to write the download to.

    +
    +
    + + pool + + +
    +

    A thread pool to use for asynchronous file I/O.

    +
    +
    + + reportHead + + +
    +

    A closure called when the response head is available.

    +
    +
    + + reportProgress + + +
    +

    A closure called when a body chunk has been downloaded, with +the total byte count and download byte count passed to it as arguments. The callbacks +will be invoked in the same threading context that the delegate itself is invoked, +as controlled by EventLoopPreference.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveHead(
    +    task: HTTPClient.Task<Response>,
    +    _ head: HTTPResponseHead
    +) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveBodyPart(
    +    task: HTTPClient.Task<Response>,
    +    _ buffer: ByteBuffer
    +) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveError(task: HTTPClient.Task<Progress>, _ error: Error)
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didFinishRequest(task: HTTPClient.Task<Response>) throws -> Response
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.5.1/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html b/docs/1.5.1/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html new file mode 100644 index 000000000..b5296738a --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html @@ -0,0 +1,242 @@ + + + + Progress Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Progress

+
+
+ +
public struct Progress
+ +
+
+

The response type for this delegate: the total count of bytes as reported by the response +“Content-Length” header (if available) and the count of bytes downloaded.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + totalBytes + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var totalBytes: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + receivedBytes + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var receivedBytes: Int
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient.html b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient.html new file mode 100644 index 000000000..dbb60f5c5 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient.html @@ -0,0 +1,2473 @@ + + + + HTTPClient Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClient

+
+
+ +
public class HTTPClient
+ +
+
+

HTTPClient class provides API for request execution.

+ +

Example:

+
    let client = HTTPClient(eventLoopGroupProvider: .createNew)
+    client.get(url: "https://swift.org/", deadline: .now() + .seconds(1)).whenComplete { result in
+        switch result {
+        case .failure(let error):
+            // process error
+        case .success(let response):
+            if let response.status == .ok {
+                // handle response
+            } else {
+                // handle remote error
+            }
+        }
+    }
+
+ +

It is important to close the client instance, for example in a defer statement, after use to cleanly shutdown the underlying NIO EventLoopGroup:

+
    try client.syncShutdown()
+
+ + +
+
+ +
+
+
+
    +
  • +
    + + + + eventLoopGroup + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let eventLoopGroup: EventLoopGroup
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTPClient with specified EventLoopGroup provider and configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public convenience init(eventLoopGroupProvider: EventLoopGroupProvider,
    +                        configuration: Configuration = Configuration())
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + eventLoopGroupProvider + + +
    +

    Specify how EventLoopGroup will be created.

    +
    +
    + + configuration + + +
    +

    Client configuration.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTPClient with specified EventLoopGroup provider and configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public required init(eventLoopGroupProvider: EventLoopGroupProvider,
    +                     configuration: Configuration = Configuration(),
    +                     backgroundActivityLogger: Logger)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + eventLoopGroupProvider + + +
    +

    Specify how EventLoopGroup will be created.

    +
    +
    + + configuration + + +
    +

    Client configuration.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + syncShutdown() + +
    +
    +
    +
    +
    +
    +

    Shuts down the client and EventLoopGroup if it was created by the client.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func syncShutdown() throws
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + shutdown(queue:_:) + +
    +
    +
    +
    +
    +
    +

    Shuts down the client and event loop gracefully. This function is clearly an outlier in that it uses a completion +callback instead of an EventLoopFuture. The reason for that is that NIO’s EventLoopFutures will call back on an event loop. +The virtue of this function is to shut the event loop down. To work around that we call back on a DispatchQueue +instead.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func shutdown(queue: DispatchQueue = .global(), _ callback: @escaping (Error?) -> Void)
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + get(url:deadline:) + +
    +
    +
    +
    +
    +
    +

    Execute GET request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func get(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute GET request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func get(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute POST request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute POST request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PATCH request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PATCH request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PUT request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PUT request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + delete(url:deadline:) + +
    +
    +
    +
    +
    +
    +

    Execute DELETE request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func delete(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    The time when the request must have been completed by.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute DELETE request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func delete(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    The time when the request must have been completed by.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + url + + +
    +

    Request url.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP+UNIX request to a unix domain socket path, using the specified URL as the request to send to the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, socketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + urlPath + + +
    +

    The URL path and query that will be sent to the server.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTPS+UNIX request to a unix domain socket path over TLS, using the specified URL as the request to send to the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, secureSocketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + secureSocketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + urlPath + + +
    +

    The URL path and query that will be sent to the server.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, eventLoop: EventLoopPreference, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request,
    +                    eventLoop eventLoopPreference: EventLoopPreference,
    +                    deadline: NIODeadline? = nil,
    +                    logger: Logger?) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          deadline: NIODeadline? = nil) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          deadline: NIODeadline? = nil,
    +                                                          logger: Logger) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          eventLoop eventLoopPreference: EventLoopPreference,
    +                                                          deadline: NIODeadline? = nil) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          eventLoop eventLoopPreference: EventLoopPreference,
    +                                                          deadline: NIODeadline? = nil,
    +                                                          logger originalLogger: Logger?) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + Configuration + +
    +
    +
    +
    +
    +
    +

    HTTPClient configuration.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Configuration
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum EventLoopGroupProvider
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + EventLoopPreference + +
    +
    +
    +
    +
    +
    +

    Specifies how the library will treat event loop passed by the user.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct EventLoopPreference
    +
    extension HTTPClient.EventLoopPreference: CustomStringConvertible
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Decompression + +
    +
    +
    +
    +
    +
    +

    Specifies decompression settings.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum Decompression
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Cookie + +
    +
    +
    +
    +
    +
    +

    A representation of an HTTP cookie.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Cookie
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Represent HTTP response.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Response
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Body + +
    +
    +
    +
    +
    +
    +

    Represent request body.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Body
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Request + +
    +
    +
    +
    +
    +
    +

    Represent HTTP request.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Request
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Authorization + +
    +
    +
    +
    +
    +
    +

    HTTP authentication

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Authorization : Hashable
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Task + +
    +
    +
    +
    +
    +
    +

    Response execution context. Will be created by the library and could be used for obtaining +EventLoopFuture<Response> of the execution or cancellation of the execution.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class Task<Response>
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + NWPOSIXError + +
    +
    +
    +
    +
    +
    + + See more +
    +
    +
    +
  • +
  • +
    + + + + NWTLSError + +
    +
    +
    +
    +
    +
    + + See more +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Authorization.html b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Authorization.html new file mode 100644 index 000000000..fcdcee232 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Authorization.html @@ -0,0 +1,301 @@ + + + + Authorization Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Authorization

+
+
+ +
public struct Authorization : Hashable
+ +
+
+

HTTP authentication

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + +
+ diff --git a/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Body.html b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Body.html new file mode 100644 index 000000000..1991ae4ed --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Body.html @@ -0,0 +1,482 @@ + + + + Body Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Body

+
+
+ +
public struct Body
+ +
+
+

Represent request body.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + StreamWriter + +
    +
    +
    +
    +
    +
    +

    Chunk provider.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct StreamWriter
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + length + +
    +
    +
    +
    +
    +
    +

    Body size. Request validation will be failed with HTTPClientErrors.contentLengthMissing if nil, +unless Trasfer-Encoding: chunked header is set.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var length: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + stream + +
    +
    +
    +
    +
    +
    +

    Body chunk provider.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var stream: (StreamWriter) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + byteBuffer(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using ByteBuffer.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func byteBuffer(_ buffer: ByteBuffer) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + buffer + + +
    +

    Body ByteBuffer representation.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + stream(length:_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using StreamWriter.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func stream(length: Int? = nil, _ stream: @escaping (StreamWriter) -> EventLoopFuture<Void>) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + length + + +
    +

    Body size. Request validation will be failed with HTTPClientErrors.contentLengthMissing if nil, + unless Transfer-Encoding: chunked header is set.

    +
    +
    + + stream + + +
    +

    Body chunk provider.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + data(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using Data.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func data(_ data: Data) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    Body Data representation.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + string(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using String.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func string(_ string: String) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + string + + +
    +

    Body String representation.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html new file mode 100644 index 000000000..9ed00142f --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html @@ -0,0 +1,279 @@ + + + + StreamWriter Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

StreamWriter

+
+
+ +
public struct StreamWriter
+ +
+
+

Chunk provider.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + init(closure:) + +
    +
    +
    +
    +
    +
    +

    Create new StreamWriter

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(closure: @escaping (IOData) -> EventLoopFuture<Void>)
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + closure + + +
    +

    function that will be called to write actual bytes to the channel.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + write(_:) + +
    +
    +
    +
    +
    +
    +

    Write data to server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func write(_ data: IOData) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    IOData to write.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + +
+ diff --git a/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Configuration.html b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Configuration.html new file mode 100644 index 000000000..1ffdb1985 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Configuration.html @@ -0,0 +1,715 @@ + + + + Configuration Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Configuration

+
+
+ +
public struct Configuration
+ +
+
+

HTTPClient configuration.

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html new file mode 100644 index 000000000..79aa481e4 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html @@ -0,0 +1,241 @@ + + + + ConnectionPool Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

ConnectionPool

+
+
+ +
public struct ConnectionPool : Hashable
+ +
+
+

Connection pool configuration.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + idleTimeout + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var idleTimeout: TimeAmount
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + init(idleTimeout:) + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(idleTimeout: TimeAmount = .seconds(60))
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html new file mode 100644 index 000000000..e34bfc290 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html @@ -0,0 +1,479 @@ + + + + Proxy Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Proxy

+
+
+ +
public struct Proxy
+ +
+
+

Proxy server configuration +Specifies the remote address of an HTTP proxy.

+ +

Adding an Proxy to your client’s HTTPClient.Configuration +will cause requests to be passed through the specified proxy using the +HTTP CONNECT method.

+ +

If a TLSConfiguration is used in conjunction with HTTPClient.Configuration.Proxy, +TLS will be established after successful proxy, between your client +and the destination server.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server host.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + port + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server port.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var port: Int
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + authorization + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server authorization.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var authorization: HTTPClient.Authorization? { get set }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + server(host:port:) + +
    +
    +
    +
    +
    +
    +

    Create a HTTP proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func server(host: String, port: Int) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + host + + +
    +

    proxy server host.

    +
    +
    + + port + + +
    +

    proxy server port.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a HTTP proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func server(host: String, port: Int, authorization: HTTPClient.Authorization? = nil) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + host + + +
    +

    proxy server host.

    +
    +
    + + port + + +
    +

    proxy server port.

    +
    +
    + + authorization + + +
    +

    proxy server authorization.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a SOCKSv5 proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func socksServer(host: String, port: Int = 1080) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + host + + +
    +

    The SOCKSv5 proxy address.

    +
    +
    + + port + + +
    +

    The SOCKSv5 proxy port, defaults to 1080.

    +
    +
    +
    +
    +

    Return Value

    +

    A new instance of Proxy configured to connect to a SOCKSv5 server.

    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html new file mode 100644 index 000000000..668479fde --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html @@ -0,0 +1,277 @@ + + + + RedirectConfiguration Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

RedirectConfiguration

+
+
+ +
public struct RedirectConfiguration
+ +
+
+

Specifies redirect processing settings.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + disallow + +
    +
    +
    +
    +
    +
    +

    Redirects are not followed.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let disallow: HTTPClient.Configuration.RedirectConfiguration
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Redirects are followed with a specified limit.

    +
    +

    Warning

    +

    Cycle detection will keep all visited URLs in memory which means a malicious server could use this as a denial-of-service vector.

    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func follow(max: Int, allowCycles: Bool) -> RedirectConfiguration
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + max + + +
    +

    The maximum number of allowed redirects.

    +
    +
    + + allowCycles + + +
    +

    Whether cycles are allowed.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html new file mode 100644 index 000000000..1b9d79d26 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html @@ -0,0 +1,303 @@ + + + + Timeout Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Timeout

+
+
+ +
public struct Timeout
+ +
+
+

Timeout configuration.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + connect + +
    +
    +
    +
    +
    +
    +

    Specifies connect timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var connect: TimeAmount?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + read + +
    +
    +
    +
    +
    +
    +

    Specifies read timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var read: TimeAmount?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + init(connect:read:) + +
    +
    +
    +
    +
    +
    +

    Create timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(connect: TimeAmount? = nil, read: TimeAmount? = nil)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + connect + + +
    +

    connect timeout. Will default to 10 seconds, if no value is +provided. See var connectionCreationTimeout

    +
    +
    + + read + + +
    +

    read timeout.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Cookie.html b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Cookie.html new file mode 100644 index 000000000..a7e6d2bb2 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Cookie.html @@ -0,0 +1,619 @@ + + + + Cookie Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Cookie

+
+
+ +
public struct Cookie
+ +
+
+

A representation of an HTTP cookie.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + name + +
    +
    +
    +
    +
    +
    +

    The name of the cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var name: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + value + +
    +
    +
    +
    +
    +
    +

    The cookie’s string value.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var value: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + path + +
    +
    +
    +
    +
    +
    +

    The cookie’s path.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var path: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + domain + +
    +
    +
    +
    +
    +
    +

    The domain of the cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var domain: String?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + expires + +
    +
    +
    +
    +
    +
    +

    The cookie’s expiration date.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var expires: Date?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + maxAge + +
    +
    +
    +
    +
    +
    +

    The cookie’s age in seconds.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var maxAge: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + httpOnly + +
    +
    +
    +
    +
    +
    +

    Whether the cookie should only be sent to HTTP servers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var httpOnly: Bool
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + secure + +
    +
    +
    +
    +
    +
    +

    Whether the cookie should only be sent over secure channels.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var secure: Bool
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a Cookie by parsing a Set-Cookie header.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(header: String, defaultDomain: String)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + header + + +
    +

    String representation of the Set-Cookie response header.

    +
    +
    + + defaultDomain + + +
    +

    Default domain to use if cookie was sent without one.

    +
    +
    +
    +
    +

    Return Value

    +

    nil if the header is invalid.

    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(name: String, value: String, path: String = "/", domain: String? = nil, expires: Date? = nil, maxAge: Int? = nil, httpOnly: Bool = false, secure: Bool = false)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + name + + +
    +

    The name of the cookie.

    +
    +
    + + value + + +
    +

    The cookie’s string value.

    +
    +
    + + path + + +
    +

    The cookie’s path.

    +
    +
    + + domain + + +
    +

    The domain of the cookie, defaults to nil.

    +
    +
    + + expires + + +
    +

    The cookie’s expiration date, defaults to nil.

    +
    +
    + + maxAge + + +
    +

    The cookie’s age in seconds, defaults to nil.

    +
    +
    + + httpOnly + + +
    +

    Whether this cookie should be used by HTTP servers only, defaults to false.

    +
    +
    + + secure + + +
    +

    Whether this cookie should only be sent using secure channels, defaults to false.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Decompression.html b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Decompression.html new file mode 100644 index 000000000..45a108e17 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Decompression.html @@ -0,0 +1,241 @@ + + + + Decompression Enumeration Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Decompression

+
+
+ +
public enum Decompression
+ +
+
+

Specifies decompression settings.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + disabled + +
    +
    +
    +
    +
    +
    +

    Decompression is disabled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case disabled
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + enabled(limit:) + +
    +
    +
    +
    +
    +
    +

    Decompression is enabled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case enabled(limit: NIOHTTPDecompression.DecompressionLimit)
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html new file mode 100644 index 000000000..da0a0d1e9 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html @@ -0,0 +1,241 @@ + + + + EventLoopGroupProvider Enumeration Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

EventLoopGroupProvider

+
+
+ +
public enum EventLoopGroupProvider
+ +
+
+

Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + shared(_:) + +
    +
    +
    +
    +
    +
    +

    EventLoopGroup will be provided by the user. Owner of this group is responsible for its lifecycle.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case shared(EventLoopGroup)
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + createNew + +
    +
    +
    +
    +
    +
    +

    EventLoopGroup will be created by the client. When syncShutdown is called, created EventLoopGroup will be shut down as well.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case createNew
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html new file mode 100644 index 000000000..107c1c063 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html @@ -0,0 +1,308 @@ + + + + EventLoopPreference Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

EventLoopPreference

+
+
+ +
public struct EventLoopPreference
+
extension HTTPClient.EventLoopPreference: CustomStringConvertible
+ +
+
+

Specifies how the library will treat event loop passed by the user.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + indifferent + +
    +
    +
    +
    +
    +
    +

    Event Loop will be selected by the library.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let indifferent: HTTPClient.EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + delegate(on:) + +
    +
    +
    +
    +
    +
    +

    The delegate will be run on the specified EventLoop (and the Channel if possible).

    + +

    This will call the configured delegate on eventLoop and will try to use a Channel on the same +EventLoop but will not establish a new network connection just to satisfy the EventLoop preference if +another existing connection on a different EventLoop is readily available from a connection pool.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func delegate(on eventLoop: EventLoop) -> EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The delegate and the Channel will be run on the specified EventLoop.

    + +

    Use this for use-cases where you prefer a new connection to be established over re-using an existing +connection that might be on a different EventLoop.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func delegateAndChannel(on eventLoop: EventLoop) -> EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var description: String { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/NWPOSIXError.html b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/NWPOSIXError.html new file mode 100644 index 000000000..997dffe6c --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/NWPOSIXError.html @@ -0,0 +1,226 @@ + + + + NWPOSIXError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

NWPOSIXError

+ +
+
+ +
+
+
+
    +
  • +
    + + + + errorCode + +
    +
    +
    +
    +
    +
    +

    POSIX error code (enum)

    + +
    +
    +
    +
  • +
  • +
    + + + + init(_:reason:) + +
    +
    +
    +
    +
    +
    +

    Initialise a NWPOSIXError

    + +
    +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/NWTLSError.html b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/NWTLSError.html new file mode 100644 index 000000000..1ce189f14 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/NWTLSError.html @@ -0,0 +1,226 @@ + + + + NWTLSError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

NWTLSError

+ +
+
+ +
+
+
+
    +
  • +
    + + + + status + +
    +
    +
    +
    +
    +
    +

    TLS error status. List of TLS errors can be found in

    + +
    +
    +
    +
  • +
  • +
    + + + + init(_:reason:) + +
    +
    +
    +
    +
    +
    +

    initialise a NWTLSError

    + +
    +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Request.html b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Request.html new file mode 100644 index 000000000..53dfb2bde --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Request.html @@ -0,0 +1,879 @@ + + + + Request Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Request

+
+
+ +
public struct Request
+ +
+
+

Represent HTTP request.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + method + +
    +
    +
    +
    +
    +
    +

    Request HTTP method, defaults to GET.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let method: HTTPMethod
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + url + +
    +
    +
    +
    +
    +
    +

    Remote URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let url: URL
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + scheme + +
    +
    +
    +
    +
    +
    +

    Remote HTTP scheme, resolved from URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let scheme: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Remote host, resolved from URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + headers + +
    +
    +
    +
    +
    +
    +

    Request custom HTTP Headers, defaults to no headers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var headers: HTTPHeaders
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + body + +
    +
    +
    +
    +
    +
    +

    Request body, defaults to no body.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var body: Body?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + tlsConfiguration + +
    +
    +
    +
    +
    +
    +

    Request-specific TLS configuration, defaults to no request-specific TLS configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var tlsConfiguration: TLSConfiguration?
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP request.

    +
    +

    Throws

    +
      +
    • invalidURL if URL cannot be parsed.
    • +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: String, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + version + + +
    +

    HTTP version.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP request.

    +
    +

    Throws

    +
      +
    • invalidURL if URL cannot be parsed.
    • +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: String, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil, tlsConfiguration: TLSConfiguration?) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + version + + +
    +

    HTTP version.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + tlsConfiguration + + +
    +

    Request TLS configuration

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTP Request.

    +
    +

    Throws

    +
      +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    • missingSocketPath if URL does not contains a socketPath as an encoded host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTP Request.

    +
    +

    Throws

    +
      +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    • missingSocketPath if URL does not contains a socketPath as an encoded host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil, tlsConfiguration: TLSConfiguration?) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + tlsConfiguration + + +
    +

    Request TLS configuration

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + useTLS + +
    +
    +
    +
    +
    +
    +

    Whether request will be executed using secure socket.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var useTLS: Bool { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + port + +
    +
    +
    +
    +
    +
    +

    Resolved port.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var port: Int { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Response.html b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Response.html new file mode 100644 index 000000000..c295e655d --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Response.html @@ -0,0 +1,544 @@ + + + + Response Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Response

+
+
+ +
public struct Response
+ +
+
+

Represent HTTP response.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Remote host of the request.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + status + +
    +
    +
    +
    +
    +
    +

    Response HTTP status.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var status: HTTPResponseStatus
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + version + +
    +
    +
    +
    +
    +
    +

    Response HTTP version.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var version: HTTPVersion
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + headers + +
    +
    +
    +
    +
    +
    +

    Reponse HTTP headers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var headers: HTTPHeaders
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + body + +
    +
    +
    +
    +
    +
    +

    Response body.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var body: ByteBuffer?
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP Response.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    @available(*, deprecated, renamed: "init(host:status:version:headers:body:﹚")
    +public init(host: String, status: HTTPResponseStatus, headers: HTTPHeaders, body: ByteBuffer?)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + host + + +
    +

    Remote host of the request.

    +
    +
    + + status + + +
    +

    Response HTTP status.

    +
    +
    + + headers + + +
    +

    Reponse HTTP headers.

    +
    +
    + + body + + +
    +

    Response body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP Response.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(host: String, status: HTTPResponseStatus, version: HTTPVersion, headers: HTTPHeaders, body: ByteBuffer?)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + host + + +
    +

    Remote host of the request.

    +
    +
    + + status + + +
    +

    Response HTTP status.

    +
    +
    + + version + + +
    +

    Response HTTP version.

    +
    +
    + + headers + + +
    +

    Reponse HTTP headers.

    +
    +
    + + body + + +
    +

    Response body.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + cookies + +
    +
    +
    +
    +
    +
    +

    List of HTTP cookies returned by the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var cookies: [HTTPClient.Cookie] { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Task.html b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Task.html new file mode 100644 index 000000000..20bd577f3 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClient/Task.html @@ -0,0 +1,311 @@ + + + + Task Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Task

+
+
+ +
public final class Task<Response>
+ +
+
+

Response execution context. Will be created by the library and could be used for obtaining +EventLoopFuture<Response> of the execution or cancellation of the execution.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + eventLoop + +
    +
    +
    +
    +
    +
    +

    The EventLoop the delegate will be executed on.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let eventLoop: EventLoop
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + futureResult + +
    +
    +
    +
    +
    +
    +

    EventLoopFuture for the response returned by this request.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var futureResult: EventLoopFuture<Response> { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + wait() + +
    +
    +
    +
    +
    +
    +

    Waits for execution of this request to complete.

    +
    +

    Throws

    + The error value of the EventLoopFuture if it errors. + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func wait() throws -> Response
    + +
    +
    +
    +

    Return Value

    +

    The value of the EventLoopFuture when it completes.

    +
    + +
    +
    +
  • +
  • +
    + + + + cancel() + +
    +
    +
    +
    +
    +
    +

    Cancels the request execution.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func cancel()
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html new file mode 100644 index 000000000..48d2e754c --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html @@ -0,0 +1,299 @@ + + + + HTTPClientCopyingDelegate Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientCopyingDelegate

+
+
+ +
public final class HTTPClientCopyingDelegate : HTTPClientResponseDelegate
+ +
+
+

Undocumented

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/Classes/ResponseAccumulator.html b/docs/1.5.1/AsyncHTTPClient/Classes/ResponseAccumulator.html new file mode 100644 index 000000000..38b5d0a9b --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Classes/ResponseAccumulator.html @@ -0,0 +1,357 @@ + + + + ResponseAccumulator Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

ResponseAccumulator

+
+
+ +
public class ResponseAccumulator : HTTPClientResponseDelegate
+ +
+
+

Undocumented

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/Extensions.html b/docs/1.5.1/AsyncHTTPClient/Extensions.html new file mode 100644 index 000000000..080ba23fe --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Extensions.html @@ -0,0 +1,216 @@ + + + + Extensions Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Extensions

+

The following extensions are available globally.

+ +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/Extensions/URL.html b/docs/1.5.1/AsyncHTTPClient/Extensions/URL.html new file mode 100644 index 000000000..7bcdf3ca5 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Extensions/URL.html @@ -0,0 +1,299 @@ + + + + URL Extension Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

URL

+
+
+ +
extension URL
+ +
+
+ +
+
+ +
+
+
+
    +
  • + +
    +
    +
    +
    +
    +

    Initializes a newly created HTTP URL connecting to a unix domain socket path. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “http+unix” scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(httpURLWithSocketPath socketPath: String, uri: String = "/")
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + uri + + +
    +

    The URI path and query that will be sent to the server.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Initializes a newly created HTTPS URL connecting to a unix domain socket path over TLS. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “https+unix” scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(httpsURLWithSocketPath socketPath: String, uri: String = "/")
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + uri + + +
    +

    The URI path and query that will be sent to the server.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/Protocols.html b/docs/1.5.1/AsyncHTTPClient/Protocols.html new file mode 100644 index 000000000..cf55c0985 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Protocols.html @@ -0,0 +1,234 @@ + + + + Protocols Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Protocols

+

The following protocols are available globally.

+ +
+
+ +
+
+
+
    +
  • + +
    +
    +
    +
    +
    +

    HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed. +You can implement this protocol if you need fine-grained control over an HTTP request/response, for example, if you want to inspect the response +headers before deciding whether to accept a response body, or if you want to stream your request body. Pass an instance of your conforming +class to the HTTPClient.execute() method and this package will call each delegate method appropriately as the request takes place./

    +

    Backpressure

    + +

    A HTTPClientResponseDelegate can be used to exert backpressure on the server response. This is achieved by way of the futures returned from +didReceiveHead and didReceiveBodyPart. The following functions are part of the “backpressure system” in the delegate:

    + +
      +
    • didReceiveHead
    • +
    • didReceiveBodyPart
    • +
    • didFinishRequest
    • +
    • didReceiveError
    • +
    + +

    The first three methods are strictly exclusive, with that exclusivity managed by the futures returned by didReceiveHead and +didReceiveBodyPart. What this means is that until the returned future is completed, none of these three methods will be called +again. This allows delegates to rate limit the server to a capacity it can manage. didFinishRequest does not return a future, +as we are expecting no more data from the server at this time.

    + +

    didReceiveError is somewhat special: it signals the end of this regime. didRecieveError is not exclusive: it may be called at +any time, even if a returned future is not yet completed. didReceiveError is terminal, meaning that once it has been called none +of these four methods will be called again. This can be used as a signal to abandon all outstanding work.

    +
    +

    Note

    + This delegate is strongly held by the HTTPTaskHandler + for the duration of the Request processing and will be + released together with the HTTPTaskHandler when channel is closed. + Users of the library are not required to keep a reference to the + object that implements this protocol, but may do so if needed. + +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public protocol HTTPClientResponseDelegate : AnyObject
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html b/docs/1.5.1/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html new file mode 100644 index 000000000..ea89f7910 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html @@ -0,0 +1,716 @@ + + + + HTTPClientResponseDelegate Protocol Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientResponseDelegate

+
+
+ +
public protocol HTTPClientResponseDelegate : AnyObject
+ +
+
+

HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed. +You can implement this protocol if you need fine-grained control over an HTTP request/response, for example, if you want to inspect the response +headers before deciding whether to accept a response body, or if you want to stream your request body. Pass an instance of your conforming +class to the HTTPClient.execute() method and this package will call each delegate method appropriately as the request takes place./

+

Backpressure

+ +

A HTTPClientResponseDelegate can be used to exert backpressure on the server response. This is achieved by way of the futures returned from +didReceiveHead and didReceiveBodyPart. The following functions are part of the “backpressure system” in the delegate:

+ +
    +
  • didReceiveHead
  • +
  • didReceiveBodyPart
  • +
  • didFinishRequest
  • +
  • didReceiveError
  • +
+ +

The first three methods are strictly exclusive, with that exclusivity managed by the futures returned by didReceiveHead and +didReceiveBodyPart. What this means is that until the returned future is completed, none of these three methods will be called +again. This allows delegates to rate limit the server to a capacity it can manage. didFinishRequest does not return a future, +as we are expecting no more data from the server at this time.

+ +

didReceiveError is somewhat special: it signals the end of this regime. didRecieveError is not exclusive: it may be called at +any time, even if a returned future is not yet completed. didReceiveError is terminal, meaning that once it has been called none +of these four methods will be called again. This can be used as a signal to abandon all outstanding work.

+
+

Note

+ This delegate is strongly held by the HTTPTaskHandler + for the duration of the Request processing and will be + released together with the HTTPTaskHandler when channel is closed. + Users of the library are not required to keep a reference to the + object that implements this protocol, but may do so if needed. + +
+ + +
+
+ +
+
+
+
    +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    associatedtype Response
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequestHead(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when the request head is sent. Will be called once.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequestHead(task: HTTPClient.Task<Response>, _ head: HTTPRequestHead)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + head + + +
    +

    Request head.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequestPart(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when a part of the request body is sent. Could be called zero or more times.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequestPart(task: HTTPClient.Task<Response>, _ part: IOData)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + part + + +
    +

    Request body Part.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequest(task:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when the request is fully sent. Will be called once.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequest(task: HTTPClient.Task<Response>)
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveHead(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when response head is received. Will be called once. +You must return an EventLoopFuture<Void> that you complete when you have finished processing the body part. +You can create an already succeeded future by calling task.eventLoop.makeSucceededFuture(()).

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveHead(task: HTTPClient.Task<Response>, _ head: HTTPResponseHead) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + head + + +
    +

    Received reposonse head.

    +
    +
    +
    +
    +

    Return Value

    +

    EventLoopFuture that will be used for backpressure.

    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveBodyPart(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when part of a response body is received. Could be called zero or more times. +You must return an EventLoopFuture<Void> that you complete when you have finished processing the body part. +You can create an already succeeded future by calling task.eventLoop.makeSucceededFuture(()).

    + +

    This function will not be called until the future returned by didReceiveHead has completed.

    + +

    This function will not be called for subsequent body parts until the previous future returned by a +call to this function completes.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveBodyPart(task: HTTPClient.Task<Response>, _ buffer: ByteBuffer) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + buffer + + +
    +

    Received body Part.

    +
    +
    +
    +
    +

    Return Value

    +

    EventLoopFuture that will be used for backpressure.

    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveError(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when error was thrown during request execution. Will be called zero or one time only. Request processing will be stopped after that.

    + +

    This function may be called at any time: it does not respect the backpressure exerted by didReceiveHead and didReceiveBodyPart. +All outstanding work may be cancelled when this is received. Once called, no further calls will be made to didReceiveHead, didReceiveBodyPart, +or didFinishRequest.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveError(task: HTTPClient.Task<Response>, _ error: Error)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + error + + +
    +

    Error that occured during response processing.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Called when the complete HTTP request is finished. You must return an instance of your Response associated type. Will be called once, except if an error occurred.

    + +

    This function will not be called until all futures returned by didReceiveHead and didReceiveBodyPart have completed. Once called, +no further calls will be made to didReceiveHead, didReceiveBodyPart, or didReceiveError.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didFinishRequest(task: HTTPClient.Task<Response>) throws -> Response
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    +
    +
    +

    Return Value

    +

    Result of processing.

    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/Structs.html b/docs/1.5.1/AsyncHTTPClient/Structs.html new file mode 100644 index 000000000..cb2627efd --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Structs.html @@ -0,0 +1,202 @@ + + + + Structures Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Structures

+

The following structures are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + HTTPClientError + +
    +
    +
    +
    +
    +
    +

    Possible client errors.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct HTTPClientError : Error, Equatable, CustomStringConvertible
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/Structs/HTTPClientError.html b/docs/1.5.1/AsyncHTTPClient/Structs/HTTPClientError.html new file mode 100644 index 000000000..7b93d7c08 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/Structs/HTTPClientError.html @@ -0,0 +1,1020 @@ + + + + HTTPClientError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientError

+
+
+ +
public struct HTTPClientError : Error, Equatable, CustomStringConvertible
+ +
+
+

Possible client errors.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var description: String { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + invalidURL + +
    +
    +
    +
    +
    +
    +

    URL provided is invalid.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let invalidURL: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + emptyHost + +
    +
    +
    +
    +
    +
    +

    URL does not contain host.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let emptyHost: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + missingSocketPath + +
    +
    +
    +
    +
    +
    +

    URL does not contain a socketPath as a host for http(s)+unix shemes.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let missingSocketPath: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + alreadyShutdown + +
    +
    +
    +
    +
    +
    +

    Client is shutdown and cannot be used for new requests.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let alreadyShutdown: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + emptyScheme + +
    +
    +
    +
    +
    +
    +

    URL does not contain scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let emptyScheme: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + unsupportedScheme(_:) + +
    +
    +
    +
    +
    +
    +

    Provided URL scheme is not supported, supported schemes are: http and https

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func unsupportedScheme(_ scheme: String) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + readTimeout + +
    +
    +
    +
    +
    +
    +

    Request timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let readTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Remote connection was closed unexpectedly.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let remoteConnectionClosed: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + cancelled + +
    +
    +
    +
    +
    +
    +

    Request was cancelled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let cancelled: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Request contains invalid identity encoding.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let identityCodingIncorrectlyPresent: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Request contains multiple chunks definitions.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let chunkedSpecifiedMultipleTimes: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + invalidProxyResponse + +
    +
    +
    +
    +
    +
    +

    Proxy response was invalid.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let invalidProxyResponse: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + contentLengthMissing + +
    +
    +
    +
    +
    +
    +

    Request does not contain Content-Length header.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let contentLengthMissing: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Proxy Authentication Required.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let proxyAuthenticationRequired: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + redirectLimitReached + +
    +
    +
    +
    +
    +
    +

    Redirect Limit reached.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let redirectLimitReached: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + redirectCycleDetected + +
    +
    +
    +
    +
    +
    +

    Redirect Cycle detected.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let redirectCycleDetected: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + uncleanShutdown + +
    +
    +
    +
    +
    +
    +

    Unclean shutdown.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let uncleanShutdown: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + traceRequestWithBody + +
    +
    +
    +
    +
    +
    +

    A body was sent in a request with method TRACE.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let traceRequestWithBody: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Header field names contain invalid characters.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func invalidHeaderFieldNames(_ names: [String]) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + bodyLengthMismatch + +
    +
    +
    +
    +
    +
    +

    Body length is not equal to Content-Length.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let bodyLengthMismatch: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + writeAfterRequestSent + +
    +
    +
    +
    +
    +
    +

    Body part was written after request was fully sent.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let writeAfterRequestSent: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + incompatibleHeaders + +
    +
    +
    +
    +
    +
    +

    Incompatible headers specified, for example Transfer-Encoding and Content-Length.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let incompatibleHeaders: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + connectTimeout + +
    +
    +
    +
    +
    +
    +

    Creating a new tcp connection timed out

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let connectTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + socksHandshakeTimeout + +
    +
    +
    +
    +
    +
    +

    The socks handshake timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let socksHandshakeTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The http proxy connection creation timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let httpProxyHandshakeTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + tlsHandshakeTimeout + +
    +
    +
    +
    +
    +
    +

    The tls handshake timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let tlsHandshakeTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The remote server only offered an unsupported application protocol

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func serverOfferedUnsupportedApplicationProtocol(_ proto: String) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/badge.svg b/docs/1.5.1/AsyncHTTPClient/badge.svg new file mode 100644 index 000000000..b28dab66c --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/badge.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + documentation + + + documentation + + + 89% + + + 89% + + + diff --git a/docs/1.5.1/AsyncHTTPClient/css/highlight.css b/docs/1.5.1/AsyncHTTPClient/css/highlight.css new file mode 100644 index 000000000..c170357ce --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/css/highlight.css @@ -0,0 +1,202 @@ +/*! Jazzy - https://github.com/realm/jazzy + * Copyright Realm Inc. + * SPDX-License-Identifier: MIT + */ +/* Credit to https://gist.github.com/wataru420/2048287 */ +.highlight .c { + color: #999988; + font-style: italic; } + +.highlight .err { + color: #a61717; + background-color: #e3d2d2; } + +.highlight .k { + color: #000000; + font-weight: bold; } + +.highlight .o { + color: #000000; + font-weight: bold; } + +.highlight .cm { + color: #999988; + font-style: italic; } + +.highlight .cp { + color: #999999; + font-weight: bold; } + +.highlight .c1 { + color: #999988; + font-style: italic; } + +.highlight .cs { + color: #999999; + font-weight: bold; + font-style: italic; } + +.highlight .gd { + color: #000000; + background-color: #ffdddd; } + +.highlight .gd .x { + color: #000000; + background-color: #ffaaaa; } + +.highlight .ge { + color: #000000; + font-style: italic; } + +.highlight .gr { + color: #aa0000; } + +.highlight .gh { + color: #999999; } + +.highlight .gi { + color: #000000; + background-color: #ddffdd; } + +.highlight .gi .x { + color: #000000; + background-color: #aaffaa; } + +.highlight .go { + color: #888888; } + +.highlight .gp { + color: #555555; } + +.highlight .gs { + font-weight: bold; } + +.highlight .gu { + color: #aaaaaa; } + +.highlight .gt { + color: #aa0000; } + +.highlight .kc { + color: #000000; + font-weight: bold; } + +.highlight .kd { + color: #000000; + font-weight: bold; } + +.highlight .kp { + color: #000000; + font-weight: bold; } + +.highlight .kr { + color: #000000; + font-weight: bold; } + +.highlight .kt { + color: #445588; } + +.highlight .m { + color: #009999; } + +.highlight .s { + color: #d14; } + +.highlight .na { + color: #008080; } + +.highlight .nb { + color: #0086B3; } + +.highlight .nc { + color: #445588; + font-weight: bold; } + +.highlight .no { + color: #008080; } + +.highlight .ni { + color: #800080; } + +.highlight .ne { + color: #990000; + font-weight: bold; } + +.highlight .nf { + color: #990000; } + +.highlight .nn { + color: #555555; } + +.highlight .nt { + color: #000080; } + +.highlight .nv { + color: #008080; } + +.highlight .ow { + color: #000000; + font-weight: bold; } + +.highlight .w { + color: #bbbbbb; } + +.highlight .mf { + color: #009999; } + +.highlight .mh { + color: #009999; } + +.highlight .mi { + color: #009999; } + +.highlight .mo { + color: #009999; } + +.highlight .sb { + color: #d14; } + +.highlight .sc { + color: #d14; } + +.highlight .sd { + color: #d14; } + +.highlight .s2 { + color: #d14; } + +.highlight .se { + color: #d14; } + +.highlight .sh { + color: #d14; } + +.highlight .si { + color: #d14; } + +.highlight .sx { + color: #d14; } + +.highlight .sr { + color: #009926; } + +.highlight .s1 { + color: #d14; } + +.highlight .ss { + color: #990073; } + +.highlight .bp { + color: #999999; } + +.highlight .vc { + color: #008080; } + +.highlight .vg { + color: #008080; } + +.highlight .vi { + color: #008080; } + +.highlight .il { + color: #009999; } diff --git a/docs/1.5.1/AsyncHTTPClient/css/jazzy.css b/docs/1.5.1/AsyncHTTPClient/css/jazzy.css new file mode 100644 index 000000000..c7bb9fe22 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/css/jazzy.css @@ -0,0 +1,404 @@ +/*! Jazzy - https://github.com/realm/jazzy + * Copyright Realm Inc. + * SPDX-License-Identifier: MIT + */ +*, *:before, *:after { + box-sizing: inherit; } + +body { + margin: 0; + background: #fff; + color: #333; + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + letter-spacing: .2px; + -webkit-font-smoothing: antialiased; + box-sizing: border-box; } + +h1 { + font-size: 2rem; + font-weight: 700; + margin: 1.275em 0 0.6em; } + +h2 { + font-size: 1.75rem; + font-weight: 700; + margin: 1.275em 0 0.3em; } + +h3 { + font-size: 1.5rem; + font-weight: 700; + margin: 1em 0 0.3em; } + +h4 { + font-size: 1.25rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h5 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h6 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; + color: #777; } + +p { + margin: 0 0 1em; } + +ul, ol { + padding: 0 0 0 2em; + margin: 0 0 0.85em; } + +blockquote { + margin: 0 0 0.85em; + padding: 0 15px; + color: #858585; + border-left: 4px solid #e5e5e5; } + +img { + max-width: 100%; } + +a { + color: #4183c4; + text-decoration: none; } + a:hover, a:focus { + outline: 0; + text-decoration: underline; } + a.discouraged { + text-decoration: line-through; } + a.discouraged:hover, a.discouraged:focus { + text-decoration: underline line-through; } + +table { + background: #fff; + width: 100%; + border-collapse: collapse; + border-spacing: 0; + overflow: auto; + margin: 0 0 0.85em; } + +tr:nth-child(2n) { + background-color: #fbfbfb; } + +th, td { + padding: 6px 13px; + border: 1px solid #ddd; } + +hr { + height: 1px; + border: none; + background-color: #ddd; } + +pre { + margin: 0 0 1.275em; + padding: .85em 1em; + overflow: auto; + background: #f7f7f7; + font-size: .85em; + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +code { + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +.item-container p > code, .item-container li > code, .top-matter p > code, .top-matter li > code { + background: #f7f7f7; + padding: .2em; } + .item-container p > code:before, .item-container p > code:after, .item-container li > code:before, .item-container li > code:after, .top-matter p > code:before, .top-matter p > code:after, .top-matter li > code:before, .top-matter li > code:after { + letter-spacing: -.2em; + content: "\00a0"; } + +pre code { + padding: 0; + white-space: pre; } + +.content-wrapper { + display: flex; + flex-direction: column; } + @media (min-width: 768px) { + .content-wrapper { + flex-direction: row; } } +.header { + display: flex; + padding: 8px; + font-size: 0.875em; + background: #444; + color: #999; } + +.header-col { + margin: 0; + padding: 0 8px; } + +.header-col--primary { + flex: 1; } + +.header-link { + color: #fff; } + +.header-icon { + padding-right: 2px; + vertical-align: -3px; + height: 16px; } + +.breadcrumbs { + font-size: 0.875em; + padding: 8px 16px; + margin: 0; + background: #fbfbfb; + border-bottom: 1px solid #ddd; } + +.carat { + height: 10px; + margin: 0 5px; } + +.navigation { + order: 2; } + @media (min-width: 768px) { + .navigation { + order: 1; + width: 25%; + max-width: 300px; + padding-bottom: 64px; + overflow: hidden; + word-wrap: normal; + background: #fbfbfb; + border-right: 1px solid #ddd; } } +.nav-groups { + list-style-type: none; + padding-left: 0; } + +.nav-group-name { + border-bottom: 1px solid #ddd; + padding: 8px 0 8px 16px; } + +.nav-group-name-link { + color: #333; } + +.nav-group-tasks { + margin: 8px 0; + padding: 0 0 0 8px; } + +.nav-group-task { + font-size: 1em; + list-style-type: none; + white-space: nowrap; } + +.nav-group-task-link { + color: #808080; } + +.main-content { + order: 1; } + @media (min-width: 768px) { + .main-content { + order: 2; + flex: 1; + padding-bottom: 60px; } } +.section { + padding: 0 32px; + border-bottom: 1px solid #ddd; } + +.section-content { + max-width: 834px; + margin: 0 auto; + padding: 16px 0; } + +.section-name { + color: #666; + display: block; } + .section-name p { + margin-bottom: inherit; } + +.declaration .highlight { + overflow-x: initial; + padding: 8px 0; + margin: 0; + background-color: transparent; + border: none; } + +.task-group-section { + border-top: 1px solid #ddd; } + +.task-group { + padding-top: 0px; } + +.task-name-container a[name]:before { + content: ""; + display: block; } + +.section-name-container { + position: relative; } + .section-name-container .section-name-link { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + margin-bottom: 0; } + .section-name-container .section-name { + position: relative; + pointer-events: none; + z-index: 1; } + .section-name-container .section-name a { + pointer-events: auto; } + +.item-container { + padding: 0; } + +.item { + padding-top: 8px; + width: 100%; + list-style-type: none; } + .item a[name]:before { + content: ""; + display: block; } + .item .token, .item .direct-link { + display: inline-block; + text-indent: -20px; + padding-left: 3px; + margin-left: 20px; + font-size: 1rem; } + .item .declaration-note { + font-size: .85em; + color: #808080; + font-style: italic; } + +.pointer-container { + border-bottom: 1px solid #ddd; + left: -23px; + padding-bottom: 13px; + position: relative; + width: 110%; } + +.pointer { + left: 21px; + top: 7px; + display: block; + position: absolute; + width: 12px; + height: 12px; + border-left: 1px solid #ddd; + border-top: 1px solid #ddd; + background: #fff; + transform: rotate(45deg); } + +.height-container { + display: none; + position: relative; + width: 100%; + overflow: hidden; } + .height-container .section { + background: #fff; + border: 1px solid #ddd; + border-top-width: 0; + padding-top: 10px; + padding-bottom: 5px; + padding: 8px 16px; } + +.aside, .language { + padding: 6px 12px; + margin: 12px 0; + border-left: 5px solid #dddddd; + overflow-y: hidden; } + .aside .aside-title, .language .aside-title { + font-size: 9px; + letter-spacing: 2px; + text-transform: uppercase; + padding-bottom: 0; + margin: 0; + color: #aaa; + -webkit-user-select: none; } + .aside p:last-child, .language p:last-child { + margin-bottom: 0; } + +.language { + border-left: 5px solid #cde9f4; } + .language .aside-title { + color: #4183c4; } + +.aside-warning, .aside-deprecated, .aside-unavailable { + border-left: 5px solid #ff6666; } + .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { + color: #ff0000; } + +.graybox { + border-collapse: collapse; + width: 100%; } + .graybox p { + margin: 0; + word-break: break-word; + min-width: 50px; } + .graybox td { + border: 1px solid #ddd; + padding: 5px 25px 5px 10px; + vertical-align: middle; } + .graybox tr td:first-of-type { + text-align: right; + padding: 7px; + vertical-align: top; + word-break: normal; + width: 40px; } + +.slightly-smaller { + font-size: 0.9em; } + +.footer { + padding: 8px 16px; + background: #444; + color: #ddd; + font-size: 0.8em; } + .footer p { + margin: 8px 0; } + .footer a { + color: #fff; } + +html.dash .header, html.dash .breadcrumbs, html.dash .navigation { + display: none; } + +html.dash .height-container { + display: block; } + +form[role=search] input { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 24px; + padding: 0 10px; + margin: 0; + border: none; + border-radius: 1em; } + .loading form[role=search] input { + background: white url(http://github.com/img/spinner.gif) center right 4px no-repeat; } + +form[role=search] .tt-menu { + margin: 0; + min-width: 300px; + background: #fbfbfb; + color: #333; + border: 1px solid #ddd; } + +form[role=search] .tt-highlight { + font-weight: bold; } + +form[role=search] .tt-suggestion { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 0 8px; } + form[role=search] .tt-suggestion span { + display: table-cell; + white-space: nowrap; } + form[role=search] .tt-suggestion .doc-parent-name { + width: 100%; + text-align: right; + font-weight: normal; + font-size: 0.9em; + padding-left: 16px; } + +form[role=search] .tt-suggestion:hover, +form[role=search] .tt-suggestion.tt-cursor { + cursor: pointer; + background-color: #4183c4; + color: #fff; } + +form[role=search] .tt-suggestion:hover .doc-parent-name, +form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { + color: #fff; } diff --git a/docs/1.5.1/AsyncHTTPClient/img/carat.png b/docs/1.5.1/AsyncHTTPClient/img/carat.png new file mode 100755 index 000000000..29d2f7fd4 Binary files /dev/null and b/docs/1.5.1/AsyncHTTPClient/img/carat.png differ diff --git a/docs/1.5.1/AsyncHTTPClient/img/dash.png b/docs/1.5.1/AsyncHTTPClient/img/dash.png new file mode 100755 index 000000000..6f694c7a0 Binary files /dev/null and b/docs/1.5.1/AsyncHTTPClient/img/dash.png differ diff --git a/docs/1.5.1/AsyncHTTPClient/img/gh.png b/docs/1.5.1/AsyncHTTPClient/img/gh.png new file mode 100755 index 000000000..628da97c7 Binary files /dev/null and b/docs/1.5.1/AsyncHTTPClient/img/gh.png differ diff --git a/docs/1.5.1/AsyncHTTPClient/img/spinner.gif b/docs/1.5.1/AsyncHTTPClient/img/spinner.gif new file mode 100644 index 000000000..e3038d0a4 Binary files /dev/null and b/docs/1.5.1/AsyncHTTPClient/img/spinner.gif differ diff --git a/docs/1.5.1/AsyncHTTPClient/index.html b/docs/1.5.1/AsyncHTTPClient/index.html new file mode 100644 index 000000000..a2a1e6f97 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/index.html @@ -0,0 +1,168 @@ + + + + AsyncHTTPClient Reference + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.5.1 Docs + + (89% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +

+ + + Install in Dash + +

+
+ + + +
+ +
+ +
+
+ +

AsyncHTTPClient Docs

+ +

AsyncHTTPClient is a Swift HTTP Client package.

+ +

To get started with AsyncHTTPClient, import AsyncHTTPClient. The +most important type is HTTPClient +which you can use to emit log messages.

+ +
+
+ + +
+
+ + + + diff --git a/docs/1.5.1/AsyncHTTPClient/js/jazzy.js b/docs/1.5.1/AsyncHTTPClient/js/jazzy.js new file mode 100755 index 000000000..198441660 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/js/jazzy.js @@ -0,0 +1,74 @@ +// Jazzy - https://github.com/realm/jazzy +// Copyright Realm Inc. +// SPDX-License-Identifier: MIT + +window.jazzy = {'docset': false} +if (typeof window.dash != 'undefined') { + document.documentElement.className += ' dash' + window.jazzy.docset = true +} +if (navigator.userAgent.match(/xcode/i)) { + document.documentElement.className += ' xcode' + window.jazzy.docset = true +} + +function toggleItem($link, $content) { + var animationDuration = 300; + $link.toggleClass('token-open'); + $content.slideToggle(animationDuration); +} + +function itemLinkToContent($link) { + return $link.parent().parent().next(); +} + +// On doc load + hash-change, open any targetted item +function openCurrentItemIfClosed() { + if (window.jazzy.docset) { + return; + } + var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); + $content = itemLinkToContent($link); + if ($content.is(':hidden')) { + toggleItem($link, $content); + } +} + +$(openCurrentItemIfClosed); +$(window).on('hashchange', openCurrentItemIfClosed); + +// On item link ('token') click, toggle its discussion +$('.token').on('click', function(event) { + if (window.jazzy.docset) { + return; + } + var $link = $(this); + toggleItem($link, itemLinkToContent($link)); + + // Keeps the document from jumping to the hash. + var href = $link.attr('href'); + if (history.pushState) { + history.pushState({}, '', href); + } else { + location.hash = href; + } + event.preventDefault(); +}); + +// Clicks on links to the current, closed, item need to open the item +$("a:not('.token')").on('click', function() { + if (location == this.href) { + openCurrentItemIfClosed(); + } +}); + +// KaTeX rendering +if ("katex" in window) { + $($('.math').each( (_, element) => { + katex.render(element.textContent, element, { + displayMode: $(element).hasClass('m-block'), + throwOnError: false, + trust: true + }); + })) +} diff --git a/docs/1.5.1/AsyncHTTPClient/js/jazzy.search.js b/docs/1.5.1/AsyncHTTPClient/js/jazzy.search.js new file mode 100644 index 000000000..359cdbb8b --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/js/jazzy.search.js @@ -0,0 +1,74 @@ +// Jazzy - https://github.com/realm/jazzy +// Copyright Realm Inc. +// SPDX-License-Identifier: MIT + +$(function(){ + var $typeahead = $('[data-typeahead]'); + var $form = $typeahead.parents('form'); + var searchURL = $form.attr('action'); + + function displayTemplate(result) { + return result.name; + } + + function suggestionTemplate(result) { + var t = '
'; + t += '' + result.name + ''; + if (result.parent_name) { + t += '' + result.parent_name + ''; + } + t += '
'; + return t; + } + + $typeahead.one('focus', function() { + $form.addClass('loading'); + + $.getJSON(searchURL).then(function(searchData) { + const searchIndex = lunr(function() { + this.ref('url'); + this.field('name'); + this.field('abstract'); + for (const [url, doc] of Object.entries(searchData)) { + this.add({url: url, name: doc.name, abstract: doc.abstract}); + } + }); + + $typeahead.typeahead( + { + highlight: true, + minLength: 3, + autoselect: true + }, + { + limit: 10, + display: displayTemplate, + templates: { suggestion: suggestionTemplate }, + source: function(query, sync) { + const lcSearch = query.toLowerCase(); + const results = searchIndex.query(function(q) { + q.term(lcSearch, { boost: 100 }); + q.term(lcSearch, { + boost: 10, + wildcard: lunr.Query.wildcard.TRAILING + }); + }).map(function(result) { + var doc = searchData[result.ref]; + doc.url = result.ref; + return doc; + }); + sync(results); + } + } + ); + $form.removeClass('loading'); + $typeahead.trigger('focus'); + }); + }); + + var baseURL = searchURL.slice(0, -"search.json".length); + + $typeahead.on('typeahead:select', function(e, result) { + window.location = baseURL + result.url; + }); +}); diff --git a/docs/1.5.1/AsyncHTTPClient/js/jquery.min.js b/docs/1.5.1/AsyncHTTPClient/js/jquery.min.js new file mode 100644 index 000000000..c4c6022f2 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 00){var c=e.utils.clone(r)||{};c.position=[a,l],c.index=s.length,s.push(new e.Token(i.slice(a,o),c))}a=o+1}}return s},e.tokenizer.separator=/[\s\-]+/,e.Pipeline=function(){this._stack=[]},e.Pipeline.registeredFunctions=Object.create(null),e.Pipeline.registerFunction=function(t,r){r in this.registeredFunctions&&e.utils.warn("Overwriting existing registered function: "+r),t.label=r,e.Pipeline.registeredFunctions[t.label]=t},e.Pipeline.warnIfFunctionNotRegistered=function(t){var r=t.label&&t.label in this.registeredFunctions;r||e.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",t)},e.Pipeline.load=function(t){var r=new e.Pipeline;return t.forEach(function(t){var i=e.Pipeline.registeredFunctions[t];if(!i)throw new Error("Cannot load unregistered function: "+t);r.add(i)}),r},e.Pipeline.prototype.add=function(){var t=Array.prototype.slice.call(arguments);t.forEach(function(t){e.Pipeline.warnIfFunctionNotRegistered(t),this._stack.push(t)},this)},e.Pipeline.prototype.after=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,r)},e.Pipeline.prototype.before=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");this._stack.splice(i,0,r)},e.Pipeline.prototype.remove=function(e){var t=this._stack.indexOf(e);t!=-1&&this._stack.splice(t,1)},e.Pipeline.prototype.run=function(e){for(var t=this._stack.length,r=0;r1&&(se&&(r=n),s!=e);)i=r-t,n=t+Math.floor(i/2),s=this.elements[2*n];return s==e?2*n:s>e?2*n:sa?l+=2:o==a&&(t+=r[u+1]*i[l+1],u+=2,l+=2);return t},e.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},e.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),t=1,r=0;t0){var o,a=s.str.charAt(0);a in s.node.edges?o=s.node.edges[a]:(o=new e.TokenSet,s.node.edges[a]=o),1==s.str.length&&(o["final"]=!0),n.push({node:o,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(0!=s.editsRemaining){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new e.TokenSet;s.node.edges["*"]=u}if(0==s.str.length&&(u["final"]=!0),n.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&n.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),1==s.str.length&&(s.node["final"]=!0),s.str.length>=1){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new e.TokenSet;s.node.edges["*"]=l}1==s.str.length&&(l["final"]=!0),n.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var c,h=s.str.charAt(0),d=s.str.charAt(1);d in s.node.edges?c=s.node.edges[d]:(c=new e.TokenSet,s.node.edges[d]=c),1==s.str.length&&(c["final"]=!0),n.push({node:c,editsRemaining:s.editsRemaining-1,str:h+s.str.slice(2)})}}}return i},e.TokenSet.fromString=function(t){for(var r=new e.TokenSet,i=r,n=0,s=t.length;n=e;t--){var r=this.uncheckedNodes[t],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r["char"]]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}},e.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},e.Index.prototype.search=function(t){return this.query(function(r){var i=new e.QueryParser(t,r);i.parse()})},e.Index.prototype.query=function(t){for(var r=new e.Query(this.fields),i=Object.create(null),n=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),u=0;u1?this._b=1:this._b=e},e.Builder.prototype.k1=function(e){this._k1=e},e.Builder.prototype.add=function(t,r){var i=t[this._ref],n=Object.keys(this._fields);this._documents[i]=r||{},this.documentCount+=1;for(var s=0;s=this.length)return e.QueryLexer.EOS;var t=this.str.charAt(this.pos);return this.pos+=1,t},e.QueryLexer.prototype.width=function(){return this.pos-this.start},e.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},e.QueryLexer.prototype.backup=function(){this.pos-=1},e.QueryLexer.prototype.acceptDigitRun=function(){var t,r;do t=this.next(),r=t.charCodeAt(0);while(r>47&&r<58);t!=e.QueryLexer.EOS&&this.backup()},e.QueryLexer.prototype.more=function(){return this.pos1&&(t.backup(),t.emit(e.QueryLexer.TERM)),t.ignore(),t.more())return e.QueryLexer.lexText},e.QueryLexer.lexEditDistance=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.EDIT_DISTANCE),e.QueryLexer.lexText},e.QueryLexer.lexBoost=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.BOOST),e.QueryLexer.lexText},e.QueryLexer.lexEOS=function(t){t.width()>0&&t.emit(e.QueryLexer.TERM)},e.QueryLexer.termSeparator=e.tokenizer.separator,e.QueryLexer.lexText=function(t){for(;;){var r=t.next();if(r==e.QueryLexer.EOS)return e.QueryLexer.lexEOS;if(92!=r.charCodeAt(0)){if(":"==r)return e.QueryLexer.lexField;if("~"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexEditDistance;if("^"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexBoost;if("+"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if("-"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if(r.match(e.QueryLexer.termSeparator))return e.QueryLexer.lexTerm}else t.escapeCharacter()}},e.QueryParser=function(t,r){this.lexer=new e.QueryLexer(t),this.query=r,this.currentClause={},this.lexemeIdx=0},e.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var t=e.QueryParser.parseClause;t;)t=t(this);return this.query},e.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},e.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},e.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},e.QueryParser.parseClause=function(t){var r=t.peekLexeme();if(void 0!=r)switch(r.type){case e.QueryLexer.PRESENCE:return e.QueryParser.parsePresence;case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expected either a field or a term, found "+r.type;throw r.str.length>=1&&(i+=" with value '"+r.str+"'"),new e.QueryParseError(i,r.start,r.end)}},e.QueryParser.parsePresence=function(t){var r=t.consumeLexeme();if(void 0!=r){switch(r.str){case"-":t.currentClause.presence=e.Query.presence.PROHIBITED;break;case"+":t.currentClause.presence=e.Query.presence.REQUIRED;break;default:var i="unrecognised presence operator'"+r.str+"'";throw new e.QueryParseError(i,r.start,r.end)}var n=t.peekLexeme();if(void 0==n){var i="expecting term or field, found nothing";throw new e.QueryParseError(i,r.start,r.end)}switch(n.type){case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expecting term or field, found '"+n.type+"'";throw new e.QueryParseError(i,n.start,n.end)}}},e.QueryParser.parseField=function(t){var r=t.consumeLexeme();if(void 0!=r){if(t.query.allFields.indexOf(r.str)==-1){var i=t.query.allFields.map(function(e){return"'"+e+"'"}).join(", "),n="unrecognised field '"+r.str+"', possible fields: "+i;throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.fields=[r.str];var s=t.peekLexeme();if(void 0==s){var n="expecting term, found nothing";throw new e.QueryParseError(n,r.start,r.end)}switch(s.type){case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var n="expecting term, found '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseTerm=function(t){var r=t.consumeLexeme();if(void 0!=r){t.currentClause.term=r.str.toLowerCase(),r.str.indexOf("*")!=-1&&(t.currentClause.usePipeline=!1);var i=t.peekLexeme();if(void 0==i)return void t.nextClause();switch(i.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+i.type+"'";throw new e.QueryParseError(n,i.start,i.end)}}},e.QueryParser.parseEditDistance=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="edit distance must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.editDistance=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseBoost=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="boost must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.boost=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():e.lunr=t()}(this,function(){return e})}(); diff --git a/docs/1.5.1/AsyncHTTPClient/js/typeahead.jquery.js b/docs/1.5.1/AsyncHTTPClient/js/typeahead.jquery.js new file mode 100644 index 000000000..3a2d2ab03 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/js/typeahead.jquery.js @@ -0,0 +1,1694 @@ +/*! + * typeahead.js 1.3.1 + * https://github.com/corejavascript/typeahead.js + * Copyright 2013-2020 Twitter, Inc. and other contributors; Licensed MIT + */ + + +(function(root, factory) { + if (typeof define === "function" && define.amd) { + define([ "jquery" ], function(a0) { + return factory(a0); + }); + } else if (typeof module === "object" && module.exports) { + module.exports = factory(require("jquery")); + } else { + factory(root["jQuery"]); + } +})(this, function($) { + var _ = function() { + "use strict"; + return { + isMsie: function() { + return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false; + }, + isBlankString: function(str) { + return !str || /^\s*$/.test(str); + }, + escapeRegExChars: function(str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + }, + isString: function(obj) { + return typeof obj === "string"; + }, + isNumber: function(obj) { + return typeof obj === "number"; + }, + isArray: $.isArray, + isFunction: $.isFunction, + isObject: $.isPlainObject, + isUndefined: function(obj) { + return typeof obj === "undefined"; + }, + isElement: function(obj) { + return !!(obj && obj.nodeType === 1); + }, + isJQuery: function(obj) { + return obj instanceof $; + }, + toStr: function toStr(s) { + return _.isUndefined(s) || s === null ? "" : s + ""; + }, + bind: $.proxy, + each: function(collection, cb) { + $.each(collection, reverseArgs); + function reverseArgs(index, value) { + return cb(value, index); + } + }, + map: $.map, + filter: $.grep, + every: function(obj, test) { + var result = true; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (!(result = test.call(null, val, key, obj))) { + return false; + } + }); + return !!result; + }, + some: function(obj, test) { + var result = false; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (result = test.call(null, val, key, obj)) { + return false; + } + }); + return !!result; + }, + mixin: $.extend, + identity: function(x) { + return x; + }, + clone: function(obj) { + return $.extend(true, {}, obj); + }, + getIdGenerator: function() { + var counter = 0; + return function() { + return counter++; + }; + }, + templatify: function templatify(obj) { + return $.isFunction(obj) ? obj : template; + function template() { + return String(obj); + } + }, + defer: function(fn) { + setTimeout(fn, 0); + }, + debounce: function(func, wait, immediate) { + var timeout, result; + return function() { + var context = this, args = arguments, later, callNow; + later = function() { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + } + }; + callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + } + return result; + }; + }, + throttle: function(func, wait) { + var context, args, timeout, result, previous, later; + previous = 0; + later = function() { + previous = new Date(); + timeout = null; + result = func.apply(context, args); + }; + return function() { + var now = new Date(), remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + } else if (!timeout) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }, + stringify: function(val) { + return _.isString(val) ? val : JSON.stringify(val); + }, + guid: function() { + function _p8(s) { + var p = (Math.random().toString(16) + "000000000").substr(2, 8); + return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p; + } + return "tt-" + _p8() + _p8(true) + _p8(true) + _p8(); + }, + noop: function() {} + }; + }(); + var WWW = function() { + "use strict"; + var defaultClassNames = { + wrapper: "twitter-typeahead", + input: "tt-input", + hint: "tt-hint", + menu: "tt-menu", + dataset: "tt-dataset", + suggestion: "tt-suggestion", + selectable: "tt-selectable", + empty: "tt-empty", + open: "tt-open", + cursor: "tt-cursor", + highlight: "tt-highlight" + }; + return build; + function build(o) { + var www, classes; + classes = _.mixin({}, defaultClassNames, o); + www = { + css: buildCss(), + classes: classes, + html: buildHtml(classes), + selectors: buildSelectors(classes) + }; + return { + css: www.css, + html: www.html, + classes: www.classes, + selectors: www.selectors, + mixin: function(o) { + _.mixin(o, www); + } + }; + } + function buildHtml(c) { + return { + wrapper: '', + menu: '
' + }; + } + function buildSelectors(classes) { + var selectors = {}; + _.each(classes, function(v, k) { + selectors[k] = "." + v; + }); + return selectors; + } + function buildCss() { + var css = { + wrapper: { + position: "relative", + display: "inline-block" + }, + hint: { + position: "absolute", + top: "0", + left: "0", + borderColor: "transparent", + boxShadow: "none", + opacity: "1" + }, + input: { + position: "relative", + verticalAlign: "top", + backgroundColor: "transparent" + }, + inputWithNoHint: { + position: "relative", + verticalAlign: "top" + }, + menu: { + position: "absolute", + top: "100%", + left: "0", + zIndex: "100", + display: "none" + }, + ltr: { + left: "0", + right: "auto" + }, + rtl: { + left: "auto", + right: " 0" + } + }; + if (_.isMsie()) { + _.mixin(css.input, { + backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" + }); + } + return css; + } + }(); + var EventBus = function() { + "use strict"; + var namespace, deprecationMap; + namespace = "typeahead:"; + deprecationMap = { + render: "rendered", + cursorchange: "cursorchanged", + select: "selected", + autocomplete: "autocompleted" + }; + function EventBus(o) { + if (!o || !o.el) { + $.error("EventBus initialized without el"); + } + this.$el = $(o.el); + } + _.mixin(EventBus.prototype, { + _trigger: function(type, args) { + var $e = $.Event(namespace + type); + this.$el.trigger.call(this.$el, $e, args || []); + return $e; + }, + before: function(type) { + var args, $e; + args = [].slice.call(arguments, 1); + $e = this._trigger("before" + type, args); + return $e.isDefaultPrevented(); + }, + trigger: function(type) { + var deprecatedType; + this._trigger(type, [].slice.call(arguments, 1)); + if (deprecatedType = deprecationMap[type]) { + this._trigger(deprecatedType, [].slice.call(arguments, 1)); + } + } + }); + return EventBus; + }(); + var EventEmitter = function() { + "use strict"; + var splitter = /\s+/, nextTick = getNextTick(); + return { + onSync: onSync, + onAsync: onAsync, + off: off, + trigger: trigger + }; + function on(method, types, cb, context) { + var type; + if (!cb) { + return this; + } + types = types.split(splitter); + cb = context ? bindContext(cb, context) : cb; + this._callbacks = this._callbacks || {}; + while (type = types.shift()) { + this._callbacks[type] = this._callbacks[type] || { + sync: [], + async: [] + }; + this._callbacks[type][method].push(cb); + } + return this; + } + function onAsync(types, cb, context) { + return on.call(this, "async", types, cb, context); + } + function onSync(types, cb, context) { + return on.call(this, "sync", types, cb, context); + } + function off(types) { + var type; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + while (type = types.shift()) { + delete this._callbacks[type]; + } + return this; + } + function trigger(types) { + var type, callbacks, args, syncFlush, asyncFlush; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + args = [].slice.call(arguments, 1); + while ((type = types.shift()) && (callbacks = this._callbacks[type])) { + syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args)); + asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args)); + syncFlush() && nextTick(asyncFlush); + } + return this; + } + function getFlush(callbacks, context, args) { + return flush; + function flush() { + var cancelled; + for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) { + cancelled = callbacks[i].apply(context, args) === false; + } + return !cancelled; + } + } + function getNextTick() { + var nextTickFn; + if (window.setImmediate) { + nextTickFn = function nextTickSetImmediate(fn) { + setImmediate(function() { + fn(); + }); + }; + } else { + nextTickFn = function nextTickSetTimeout(fn) { + setTimeout(function() { + fn(); + }, 0); + }; + } + return nextTickFn; + } + function bindContext(fn, context) { + return fn.bind ? fn.bind(context) : function() { + fn.apply(context, [].slice.call(arguments, 0)); + }; + } + }(); + var highlight = function(doc) { + "use strict"; + var defaults = { + node: null, + pattern: null, + tagName: "strong", + className: null, + wordsOnly: false, + caseSensitive: false, + diacriticInsensitive: false + }; + var accented = { + A: "[AaªÀ-Åà-åĀ-ąǍǎȀ-ȃȦȧᴬᵃḀḁẚẠ-ảₐ℀℁℻⒜Ⓐⓐ㍱-㍴㎀-㎄㎈㎉㎩-㎯㏂㏊㏟㏿Aa]", + B: "[BbᴮᵇḂ-ḇℬ⒝Ⓑⓑ㍴㎅-㎇㏃㏈㏔㏝Bb]", + C: "[CcÇçĆ-čᶜ℀ℂ℃℅℆ℭⅭⅽ⒞Ⓒⓒ㍶㎈㎉㎝㎠㎤㏄-㏇Cc]", + D: "[DdĎďDŽ-džDZ-dzᴰᵈḊ-ḓⅅⅆⅮⅾ⒟Ⓓⓓ㋏㍲㍷-㍹㎗㎭-㎯㏅㏈Dd]", + E: "[EeÈ-Ëè-ëĒ-ěȄ-ȇȨȩᴱᵉḘ-ḛẸ-ẽₑ℡ℯℰⅇ⒠Ⓔⓔ㉐㋍㋎Ee]", + F: "[FfᶠḞḟ℉ℱ℻⒡Ⓕⓕ㎊-㎌㎙ff-fflFf]", + G: "[GgĜ-ģǦǧǴǵᴳᵍḠḡℊ⒢Ⓖⓖ㋌㋍㎇㎍-㎏㎓㎬㏆㏉㏒㏿Gg]", + H: "[HhĤĥȞȟʰᴴḢ-ḫẖℋ-ℎ⒣Ⓗⓗ㋌㍱㎐-㎔㏊㏋㏗Hh]", + I: "[IiÌ-Ïì-ïĨ-İIJijǏǐȈ-ȋᴵᵢḬḭỈ-ịⁱℐℑℹⅈⅠ-ⅣⅥ-ⅨⅪⅫⅰ-ⅳⅵ-ⅸⅺⅻ⒤Ⓘⓘ㍺㏌㏕fiffiIi]", + J: "[JjIJ-ĵLJ-njǰʲᴶⅉ⒥ⒿⓙⱼJj]", + K: "[KkĶķǨǩᴷᵏḰ-ḵK⒦Ⓚⓚ㎄㎅㎉㎏㎑㎘㎞㎢㎦㎪㎸㎾㏀㏆㏍-㏏Kk]", + L: "[LlĹ-ŀLJ-ljˡᴸḶḷḺ-ḽℒℓ℡Ⅼⅼ⒧Ⓛⓛ㋏㎈㎉㏐-㏓㏕㏖㏿flfflLl]", + M: "[MmᴹᵐḾ-ṃ℠™ℳⅯⅿ⒨Ⓜⓜ㍷-㍹㎃㎆㎎㎒㎖㎙-㎨㎫㎳㎷㎹㎽㎿㏁㏂㏎㏐㏔-㏖㏘㏙㏞㏟Mm]", + N: "[NnÑñŃ-ʼnNJ-njǸǹᴺṄ-ṋⁿℕ№⒩Ⓝⓝ㎁㎋㎚㎱㎵㎻㏌㏑Nn]", + O: "[OoºÒ-Öò-öŌ-őƠơǑǒǪǫȌ-ȏȮȯᴼᵒỌ-ỏₒ℅№ℴ⒪Ⓞⓞ㍵㏇㏒㏖Oo]", + P: "[PpᴾᵖṔ-ṗℙ⒫Ⓟⓟ㉐㍱㍶㎀㎊㎩-㎬㎰㎴㎺㏋㏗-㏚Pp]", + Q: "[Qqℚ⒬Ⓠⓠ㏃Qq]", + R: "[RrŔ-řȐ-ȓʳᴿᵣṘ-ṛṞṟ₨ℛ-ℝ⒭Ⓡⓡ㋍㍴㎭-㎯㏚㏛Rr]", + S: "[SsŚ-šſȘșˢṠ-ṣ₨℁℠⒮Ⓢⓢ㎧㎨㎮-㎳㏛㏜stSs]", + T: "[TtŢ-ťȚțᵀᵗṪ-ṱẗ℡™⒯Ⓣⓣ㉐㋏㎔㏏ſtstTt]", + U: "[UuÙ-Üù-üŨ-ųƯưǓǔȔ-ȗᵁᵘᵤṲ-ṷỤ-ủ℆⒰Ⓤⓤ㍳㍺Uu]", + V: "[VvᵛᵥṼ-ṿⅣ-Ⅷⅳ-ⅷ⒱Ⓥⓥⱽ㋎㍵㎴-㎹㏜㏞Vv]", + W: "[WwŴŵʷᵂẀ-ẉẘ⒲Ⓦⓦ㎺-㎿㏝Ww]", + X: "[XxˣẊ-ẍₓ℻Ⅸ-Ⅻⅸ-ⅻ⒳Ⓧⓧ㏓Xx]", + Y: "[YyÝýÿŶ-ŸȲȳʸẎẏẙỲ-ỹ⒴Ⓨⓨ㏉Yy]", + Z: "[ZzŹ-žDZ-dzᶻẐ-ẕℤℨ⒵Ⓩⓩ㎐-㎔Zz]" + }; + return function hightlight(o) { + var regex; + o = _.mixin({}, defaults, o); + if (!o.node || !o.pattern) { + return; + } + o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; + regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly, o.diacriticInsensitive); + traverse(o.node, hightlightTextNode); + function hightlightTextNode(textNode) { + var match, patternNode, wrapperNode; + if (match = regex.exec(textNode.data)) { + wrapperNode = doc.createElement(o.tagName); + o.className && (wrapperNode.className = o.className); + patternNode = textNode.splitText(match.index); + patternNode.splitText(match[0].length); + wrapperNode.appendChild(patternNode.cloneNode(true)); + textNode.parentNode.replaceChild(wrapperNode, patternNode); + } + return !!match; + } + function traverse(el, hightlightTextNode) { + var childNode, TEXT_NODE_TYPE = 3; + for (var i = 0; i < el.childNodes.length; i++) { + childNode = el.childNodes[i]; + if (childNode.nodeType === TEXT_NODE_TYPE) { + i += hightlightTextNode(childNode) ? 1 : 0; + } else { + traverse(childNode, hightlightTextNode); + } + } + } + }; + function accent_replacer(chr) { + return accented[chr.toUpperCase()] || chr; + } + function getRegex(patterns, caseSensitive, wordsOnly, diacriticInsensitive) { + var escapedPatterns = [], regexStr; + for (var i = 0, len = patterns.length; i < len; i++) { + var escapedWord = _.escapeRegExChars(patterns[i]); + if (diacriticInsensitive) { + escapedWord = escapedWord.replace(/\S/g, accent_replacer); + } + escapedPatterns.push(escapedWord); + } + regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")"; + return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); + } + }(window.document); + var Input = function() { + "use strict"; + var specialKeyCodeMap; + specialKeyCodeMap = { + 9: "tab", + 27: "esc", + 37: "left", + 39: "right", + 13: "enter", + 38: "up", + 40: "down" + }; + function Input(o, www) { + var id; + o = o || {}; + if (!o.input) { + $.error("input is missing"); + } + www.mixin(this); + this.$hint = $(o.hint); + this.$input = $(o.input); + this.$menu = $(o.menu); + id = this.$input.attr("id") || _.guid(); + this.$menu.attr("id", id + "_listbox"); + this.$hint.attr({ + "aria-hidden": true + }); + this.$input.attr({ + "aria-owns": id + "_listbox", + role: "combobox", + "aria-autocomplete": "list", + "aria-expanded": false + }); + this.query = this.$input.val(); + this.queryWhenFocused = this.hasFocus() ? this.query : null; + this.$overflowHelper = buildOverflowHelper(this.$input); + this._checkLanguageDirection(); + if (this.$hint.length === 0) { + this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; + } + this.onSync("cursorchange", this._updateDescendent); + } + Input.normalizeQuery = function(str) { + return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " "); + }; + _.mixin(Input.prototype, EventEmitter, { + _onBlur: function onBlur() { + this.resetInputValue(); + this.trigger("blurred"); + }, + _onFocus: function onFocus() { + this.queryWhenFocused = this.query; + this.trigger("focused"); + }, + _onKeydown: function onKeydown($e) { + var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; + this._managePreventDefault(keyName, $e); + if (keyName && this._shouldTrigger(keyName, $e)) { + this.trigger(keyName + "Keyed", $e); + } + }, + _onInput: function onInput() { + this._setQuery(this.getInputValue()); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + _managePreventDefault: function managePreventDefault(keyName, $e) { + var preventDefault; + switch (keyName) { + case "up": + case "down": + preventDefault = !withModifier($e); + break; + + default: + preventDefault = false; + } + preventDefault && $e.preventDefault(); + }, + _shouldTrigger: function shouldTrigger(keyName, $e) { + var trigger; + switch (keyName) { + case "tab": + trigger = !withModifier($e); + break; + + default: + trigger = true; + } + return trigger; + }, + _checkLanguageDirection: function checkLanguageDirection() { + var dir = (this.$input.css("direction") || "ltr").toLowerCase(); + if (this.dir !== dir) { + this.dir = dir; + this.$hint.attr("dir", dir); + this.trigger("langDirChanged", dir); + } + }, + _setQuery: function setQuery(val, silent) { + var areEquivalent, hasDifferentWhitespace; + areEquivalent = areQueriesEquivalent(val, this.query); + hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false; + this.query = val; + if (!silent && !areEquivalent) { + this.trigger("queryChanged", this.query); + } else if (!silent && hasDifferentWhitespace) { + this.trigger("whitespaceChanged", this.query); + } + }, + _updateDescendent: function updateDescendent(event, id) { + this.$input.attr("aria-activedescendant", id); + }, + bind: function() { + var that = this, onBlur, onFocus, onKeydown, onInput; + onBlur = _.bind(this._onBlur, this); + onFocus = _.bind(this._onFocus, this); + onKeydown = _.bind(this._onKeydown, this); + onInput = _.bind(this._onInput, this); + this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown); + if (!_.isMsie() || _.isMsie() > 9) { + this.$input.on("input.tt", onInput); + } else { + this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { + if (specialKeyCodeMap[$e.which || $e.keyCode]) { + return; + } + _.defer(_.bind(that._onInput, that, $e)); + }); + } + return this; + }, + focus: function focus() { + this.$input.focus(); + }, + blur: function blur() { + this.$input.blur(); + }, + getLangDir: function getLangDir() { + return this.dir; + }, + getQuery: function getQuery() { + return this.query || ""; + }, + setQuery: function setQuery(val, silent) { + this.setInputValue(val); + this._setQuery(val, silent); + }, + hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() { + return this.query !== this.queryWhenFocused; + }, + getInputValue: function getInputValue() { + return this.$input.val(); + }, + setInputValue: function setInputValue(value) { + this.$input.val(value); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + resetInputValue: function resetInputValue() { + this.setInputValue(this.query); + }, + getHint: function getHint() { + return this.$hint.val(); + }, + setHint: function setHint(value) { + this.$hint.val(value); + }, + clearHint: function clearHint() { + this.setHint(""); + }, + clearHintIfInvalid: function clearHintIfInvalid() { + var val, hint, valIsPrefixOfHint, isValid; + val = this.getInputValue(); + hint = this.getHint(); + valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; + isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow(); + !isValid && this.clearHint(); + }, + hasFocus: function hasFocus() { + return this.$input.is(":focus"); + }, + hasOverflow: function hasOverflow() { + var constraint = this.$input.width() - 2; + this.$overflowHelper.text(this.getInputValue()); + return this.$overflowHelper.width() >= constraint; + }, + isCursorAtEnd: function() { + var valueLength, selectionStart, range; + valueLength = this.$input.val().length; + selectionStart = this.$input[0].selectionStart; + if (_.isNumber(selectionStart)) { + return selectionStart === valueLength; + } else if (document.selection) { + range = document.selection.createRange(); + range.moveStart("character", -valueLength); + return valueLength === range.text.length; + } + return true; + }, + destroy: function destroy() { + this.$hint.off(".tt"); + this.$input.off(".tt"); + this.$overflowHelper.remove(); + this.$hint = this.$input = this.$overflowHelper = $("
"); + }, + setAriaExpanded: function setAriaExpanded(value) { + this.$input.attr("aria-expanded", value); + } + }); + return Input; + function buildOverflowHelper($input) { + return $('').css({ + position: "absolute", + visibility: "hidden", + whiteSpace: "pre", + fontFamily: $input.css("font-family"), + fontSize: $input.css("font-size"), + fontStyle: $input.css("font-style"), + fontVariant: $input.css("font-variant"), + fontWeight: $input.css("font-weight"), + wordSpacing: $input.css("word-spacing"), + letterSpacing: $input.css("letter-spacing"), + textIndent: $input.css("text-indent"), + textRendering: $input.css("text-rendering"), + textTransform: $input.css("text-transform") + }).insertAfter($input); + } + function areQueriesEquivalent(a, b) { + return Input.normalizeQuery(a) === Input.normalizeQuery(b); + } + function withModifier($e) { + return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; + } + }(); + var Dataset = function() { + "use strict"; + var keys, nameGenerator; + keys = { + dataset: "tt-selectable-dataset", + val: "tt-selectable-display", + obj: "tt-selectable-object" + }; + nameGenerator = _.getIdGenerator(); + function Dataset(o, www) { + o = o || {}; + o.templates = o.templates || {}; + o.templates.notFound = o.templates.notFound || o.templates.empty; + if (!o.source) { + $.error("missing source"); + } + if (!o.node) { + $.error("missing node"); + } + if (o.name && !isValidName(o.name)) { + $.error("invalid dataset name: " + o.name); + } + www.mixin(this); + this.highlight = !!o.highlight; + this.name = _.toStr(o.name || nameGenerator()); + this.limit = o.limit || 5; + this.displayFn = getDisplayFn(o.display || o.displayKey); + this.templates = getTemplates(o.templates, this.displayFn); + this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source; + this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async; + this._resetLastSuggestion(); + this.$el = $(o.node).attr("role", "presentation").addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); + } + Dataset.extractData = function extractData(el) { + var $el = $(el); + if ($el.data(keys.obj)) { + return { + dataset: $el.data(keys.dataset) || "", + val: $el.data(keys.val) || "", + obj: $el.data(keys.obj) || null + }; + } + return null; + }; + _.mixin(Dataset.prototype, EventEmitter, { + _overwrite: function overwrite(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (this.async && this.templates.pending) { + this._renderPending(query); + } else if (!this.async && this.templates.notFound) { + this._renderNotFound(query); + } else { + this._empty(); + } + this.trigger("rendered", suggestions, false, this.name); + }, + _append: function append(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length && this.$lastSuggestion.length) { + this._appendSuggestions(query, suggestions); + } else if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (!this.$lastSuggestion.length && this.templates.notFound) { + this._renderNotFound(query); + } + this.trigger("rendered", suggestions, true, this.name); + }, + _renderSuggestions: function renderSuggestions(query, suggestions) { + var $fragment; + $fragment = this._getSuggestionsFragment(query, suggestions); + this.$lastSuggestion = $fragment.children().last(); + this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions)); + }, + _appendSuggestions: function appendSuggestions(query, suggestions) { + var $fragment, $lastSuggestion; + $fragment = this._getSuggestionsFragment(query, suggestions); + $lastSuggestion = $fragment.children().last(); + this.$lastSuggestion.after($fragment); + this.$lastSuggestion = $lastSuggestion; + }, + _renderPending: function renderPending(query) { + var template = this.templates.pending; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _renderNotFound: function renderNotFound(query) { + var template = this.templates.notFound; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _empty: function empty() { + this.$el.empty(); + this._resetLastSuggestion(); + }, + _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) { + var that = this, fragment; + fragment = document.createDocumentFragment(); + _.each(suggestions, function getSuggestionNode(suggestion) { + var $el, context; + context = that._injectQuery(query, suggestion); + $el = $(that.templates.suggestion(context)).data(keys.dataset, that.name).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable); + fragment.appendChild($el[0]); + }); + this.highlight && highlight({ + className: this.classes.highlight, + node: fragment, + pattern: query + }); + return $(fragment); + }, + _getFooter: function getFooter(query, suggestions) { + return this.templates.footer ? this.templates.footer({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _getHeader: function getHeader(query, suggestions) { + return this.templates.header ? this.templates.header({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _resetLastSuggestion: function resetLastSuggestion() { + this.$lastSuggestion = $(); + }, + _injectQuery: function injectQuery(query, obj) { + return _.isObject(obj) ? _.mixin({ + _query: query + }, obj) : obj; + }, + update: function update(query) { + var that = this, canceled = false, syncCalled = false, rendered = 0; + this.cancel(); + this.cancel = function cancel() { + canceled = true; + that.cancel = $.noop; + that.async && that.trigger("asyncCanceled", query, that.name); + }; + this.source(query, sync, async); + !syncCalled && sync([]); + function sync(suggestions) { + if (syncCalled) { + return; + } + syncCalled = true; + suggestions = (suggestions || []).slice(0, that.limit); + rendered = suggestions.length; + that._overwrite(query, suggestions); + if (rendered < that.limit && that.async) { + that.trigger("asyncRequested", query, that.name); + } + } + function async(suggestions) { + suggestions = suggestions || []; + if (!canceled && rendered < that.limit) { + that.cancel = $.noop; + var idx = Math.abs(rendered - that.limit); + rendered += idx; + that._append(query, suggestions.slice(0, idx)); + that.async && that.trigger("asyncReceived", query, that.name); + } + } + }, + cancel: $.noop, + clear: function clear() { + this._empty(); + this.cancel(); + this.trigger("cleared"); + }, + isEmpty: function isEmpty() { + return this.$el.is(":empty"); + }, + destroy: function destroy() { + this.$el = $("
"); + } + }); + return Dataset; + function getDisplayFn(display) { + display = display || _.stringify; + return _.isFunction(display) ? display : displayFn; + function displayFn(obj) { + return obj[display]; + } + } + function getTemplates(templates, displayFn) { + return { + notFound: templates.notFound && _.templatify(templates.notFound), + pending: templates.pending && _.templatify(templates.pending), + header: templates.header && _.templatify(templates.header), + footer: templates.footer && _.templatify(templates.footer), + suggestion: templates.suggestion ? userSuggestionTemplate : suggestionTemplate + }; + function userSuggestionTemplate(context) { + var template = templates.suggestion; + return $(template(context)).attr("id", _.guid()); + } + function suggestionTemplate(context) { + return $('
').attr("id", _.guid()).text(displayFn(context)); + } + } + function isValidName(str) { + return /^[_a-zA-Z0-9-]+$/.test(str); + } + }(); + var Menu = function() { + "use strict"; + function Menu(o, www) { + var that = this; + o = o || {}; + if (!o.node) { + $.error("node is required"); + } + www.mixin(this); + this.$node = $(o.node); + this.query = null; + this.datasets = _.map(o.datasets, initializeDataset); + function initializeDataset(oDataset) { + var node = that.$node.find(oDataset.node).first(); + oDataset.node = node.length ? node : $("
").appendTo(that.$node); + return new Dataset(oDataset, www); + } + } + _.mixin(Menu.prototype, EventEmitter, { + _onSelectableClick: function onSelectableClick($e) { + this.trigger("selectableClicked", $($e.currentTarget)); + }, + _onRendered: function onRendered(type, dataset, suggestions, async) { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetRendered", dataset, suggestions, async); + }, + _onCleared: function onCleared() { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetCleared"); + }, + _propagate: function propagate() { + this.trigger.apply(this, arguments); + }, + _allDatasetsEmpty: function allDatasetsEmpty() { + return _.every(this.datasets, _.bind(function isDatasetEmpty(dataset) { + var isEmpty = dataset.isEmpty(); + this.$node.attr("aria-expanded", !isEmpty); + return isEmpty; + }, this)); + }, + _getSelectables: function getSelectables() { + return this.$node.find(this.selectors.selectable); + }, + _removeCursor: function _removeCursor() { + var $selectable = this.getActiveSelectable(); + $selectable && $selectable.removeClass(this.classes.cursor); + }, + _ensureVisible: function ensureVisible($el) { + var elTop, elBottom, nodeScrollTop, nodeHeight; + elTop = $el.position().top; + elBottom = elTop + $el.outerHeight(true); + nodeScrollTop = this.$node.scrollTop(); + nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10); + if (elTop < 0) { + this.$node.scrollTop(nodeScrollTop + elTop); + } else if (nodeHeight < elBottom) { + this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight)); + } + }, + bind: function() { + var that = this, onSelectableClick; + onSelectableClick = _.bind(this._onSelectableClick, this); + this.$node.on("click.tt", this.selectors.selectable, onSelectableClick); + this.$node.on("mouseover", this.selectors.selectable, function() { + that.setCursor($(this)); + }); + this.$node.on("mouseleave", function() { + that._removeCursor(); + }); + _.each(this.datasets, function(dataset) { + dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that); + }); + return this; + }, + isOpen: function isOpen() { + return this.$node.hasClass(this.classes.open); + }, + open: function open() { + this.$node.scrollTop(0); + this.$node.addClass(this.classes.open); + }, + close: function close() { + this.$node.attr("aria-expanded", false); + this.$node.removeClass(this.classes.open); + this._removeCursor(); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.attr("dir", dir); + }, + selectableRelativeToCursor: function selectableRelativeToCursor(delta) { + var $selectables, $oldCursor, oldIndex, newIndex; + $oldCursor = this.getActiveSelectable(); + $selectables = this._getSelectables(); + oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1; + newIndex = oldIndex + delta; + newIndex = (newIndex + 1) % ($selectables.length + 1) - 1; + newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex; + return newIndex === -1 ? null : $selectables.eq(newIndex); + }, + setCursor: function setCursor($selectable) { + this._removeCursor(); + if ($selectable = $selectable && $selectable.first()) { + $selectable.addClass(this.classes.cursor); + this._ensureVisible($selectable); + } + }, + getSelectableData: function getSelectableData($el) { + return $el && $el.length ? Dataset.extractData($el) : null; + }, + getActiveSelectable: function getActiveSelectable() { + var $selectable = this._getSelectables().filter(this.selectors.cursor).first(); + return $selectable.length ? $selectable : null; + }, + getTopSelectable: function getTopSelectable() { + var $selectable = this._getSelectables().first(); + return $selectable.length ? $selectable : null; + }, + update: function update(query) { + var isValidUpdate = query !== this.query; + if (isValidUpdate) { + this.query = query; + _.each(this.datasets, updateDataset); + } + return isValidUpdate; + function updateDataset(dataset) { + dataset.update(query); + } + }, + empty: function empty() { + _.each(this.datasets, clearDataset); + this.query = null; + this.$node.addClass(this.classes.empty); + function clearDataset(dataset) { + dataset.clear(); + } + }, + destroy: function destroy() { + this.$node.off(".tt"); + this.$node = $("
"); + _.each(this.datasets, destroyDataset); + function destroyDataset(dataset) { + dataset.destroy(); + } + } + }); + return Menu; + }(); + var Status = function() { + "use strict"; + function Status(options) { + this.$el = $("", { + role: "status", + "aria-live": "polite" + }).css({ + position: "absolute", + padding: "0", + border: "0", + height: "1px", + width: "1px", + "margin-bottom": "-1px", + "margin-right": "-1px", + overflow: "hidden", + clip: "rect(0 0 0 0)", + "white-space": "nowrap" + }); + options.$input.after(this.$el); + _.each(options.menu.datasets, _.bind(function(dataset) { + if (dataset.onSync) { + dataset.onSync("rendered", _.bind(this.update, this)); + dataset.onSync("cleared", _.bind(this.cleared, this)); + } + }, this)); + } + _.mixin(Status.prototype, { + update: function update(event, suggestions) { + var length = suggestions.length; + var words; + if (length === 1) { + words = { + result: "result", + is: "is" + }; + } else { + words = { + result: "results", + is: "are" + }; + } + this.$el.text(length + " " + words.result + " " + words.is + " available, use up and down arrow keys to navigate."); + }, + cleared: function() { + this.$el.text(""); + } + }); + return Status; + }(); + var DefaultMenu = function() { + "use strict"; + var s = Menu.prototype; + function DefaultMenu() { + Menu.apply(this, [].slice.call(arguments, 0)); + } + _.mixin(DefaultMenu.prototype, Menu.prototype, { + open: function open() { + !this._allDatasetsEmpty() && this._show(); + return s.open.apply(this, [].slice.call(arguments, 0)); + }, + close: function close() { + this._hide(); + return s.close.apply(this, [].slice.call(arguments, 0)); + }, + _onRendered: function onRendered() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onRendered.apply(this, [].slice.call(arguments, 0)); + }, + _onCleared: function onCleared() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onCleared.apply(this, [].slice.call(arguments, 0)); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl); + return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0)); + }, + _hide: function hide() { + this.$node.hide(); + }, + _show: function show() { + this.$node.css("display", "block"); + } + }); + return DefaultMenu; + }(); + var Typeahead = function() { + "use strict"; + function Typeahead(o, www) { + var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged; + o = o || {}; + if (!o.input) { + $.error("missing input"); + } + if (!o.menu) { + $.error("missing menu"); + } + if (!o.eventBus) { + $.error("missing event bus"); + } + www.mixin(this); + this.eventBus = o.eventBus; + this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; + this.input = o.input; + this.menu = o.menu; + this.enabled = true; + this.autoselect = !!o.autoselect; + this.active = false; + this.input.hasFocus() && this.activate(); + this.dir = this.input.getLangDir(); + this._hacks(); + this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this); + onFocused = c(this, "activate", "open", "_onFocused"); + onBlurred = c(this, "deactivate", "_onBlurred"); + onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed"); + onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed"); + onEscKeyed = c(this, "isActive", "_onEscKeyed"); + onUpKeyed = c(this, "isActive", "open", "_onUpKeyed"); + onDownKeyed = c(this, "isActive", "open", "_onDownKeyed"); + onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed"); + onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed"); + onQueryChanged = c(this, "_openIfActive", "_onQueryChanged"); + onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged"); + this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this); + } + _.mixin(Typeahead.prototype, { + _hacks: function hacks() { + var $input, $menu; + $input = this.input.$input || $("
"); + $menu = this.menu.$node || $("
"); + $input.on("blur.tt", function($e) { + var active, isActive, hasActive; + active = document.activeElement; + isActive = $menu.is(active); + hasActive = $menu.has(active).length > 0; + if (_.isMsie() && (isActive || hasActive)) { + $e.preventDefault(); + $e.stopImmediatePropagation(); + _.defer(function() { + $input.focus(); + }); + } + }); + $menu.on("mousedown.tt", function($e) { + $e.preventDefault(); + }); + }, + _onSelectableClicked: function onSelectableClicked(type, $el) { + this.select($el); + }, + _onDatasetCleared: function onDatasetCleared() { + this._updateHint(); + }, + _onDatasetRendered: function onDatasetRendered(type, suggestions, async, dataset) { + this._updateHint(); + if (this.autoselect) { + var cursorClass = this.selectors.cursor.substr(1); + this.menu.$node.find(this.selectors.suggestion).first().addClass(cursorClass); + } + this.eventBus.trigger("render", suggestions, async, dataset); + }, + _onAsyncRequested: function onAsyncRequested(type, dataset, query) { + this.eventBus.trigger("asyncrequest", query, dataset); + }, + _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) { + this.eventBus.trigger("asynccancel", query, dataset); + }, + _onAsyncReceived: function onAsyncReceived(type, dataset, query) { + this.eventBus.trigger("asyncreceive", query, dataset); + }, + _onFocused: function onFocused() { + this._minLengthMet() && this.menu.update(this.input.getQuery()); + }, + _onBlurred: function onBlurred() { + if (this.input.hasQueryChangedSinceLastFocus()) { + this.eventBus.trigger("change", this.input.getQuery()); + } + }, + _onEnterKeyed: function onEnterKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + if (this.select($selectable)) { + $e.preventDefault(); + $e.stopPropagation(); + } + } else if (this.autoselect) { + if (this.select(this.menu.getTopSelectable())) { + $e.preventDefault(); + $e.stopPropagation(); + } + } + }, + _onTabKeyed: function onTabKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + this.select($selectable) && $e.preventDefault(); + } else if (this.autoselect) { + if ($selectable = this.menu.getTopSelectable()) { + this.autocomplete($selectable) && $e.preventDefault(); + } + } + }, + _onEscKeyed: function onEscKeyed() { + this.close(); + }, + _onUpKeyed: function onUpKeyed() { + this.moveCursor(-1); + }, + _onDownKeyed: function onDownKeyed() { + this.moveCursor(+1); + }, + _onLeftKeyed: function onLeftKeyed() { + if (this.dir === "rtl" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); + } + }, + _onRightKeyed: function onRightKeyed() { + if (this.dir === "ltr" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); + } + }, + _onQueryChanged: function onQueryChanged(e, query) { + this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty(); + }, + _onWhitespaceChanged: function onWhitespaceChanged() { + this._updateHint(); + }, + _onLangDirChanged: function onLangDirChanged(e, dir) { + if (this.dir !== dir) { + this.dir = dir; + this.menu.setLanguageDirection(dir); + } + }, + _openIfActive: function openIfActive() { + this.isActive() && this.open(); + }, + _minLengthMet: function minLengthMet(query) { + query = _.isString(query) ? query : this.input.getQuery() || ""; + return query.length >= this.minLength; + }, + _updateHint: function updateHint() { + var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match; + $selectable = this.menu.getTopSelectable(); + data = this.menu.getSelectableData($selectable); + val = this.input.getInputValue(); + if (data && !_.isBlankString(val) && !this.input.hasOverflow()) { + query = Input.normalizeQuery(val); + escapedQuery = _.escapeRegExChars(query); + frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i"); + match = frontMatchRegEx.exec(data.val); + match && this.input.setHint(val + match[1]); + } else { + this.input.clearHint(); + } + }, + isEnabled: function isEnabled() { + return this.enabled; + }, + enable: function enable() { + this.enabled = true; + }, + disable: function disable() { + this.enabled = false; + }, + isActive: function isActive() { + return this.active; + }, + activate: function activate() { + if (this.isActive()) { + return true; + } else if (!this.isEnabled() || this.eventBus.before("active")) { + return false; + } else { + this.active = true; + this.eventBus.trigger("active"); + return true; + } + }, + deactivate: function deactivate() { + if (!this.isActive()) { + return true; + } else if (this.eventBus.before("idle")) { + return false; + } else { + this.active = false; + this.close(); + this.eventBus.trigger("idle"); + return true; + } + }, + isOpen: function isOpen() { + return this.menu.isOpen(); + }, + open: function open() { + if (!this.isOpen() && !this.eventBus.before("open")) { + this.input.setAriaExpanded(true); + this.menu.open(); + this._updateHint(); + this.eventBus.trigger("open"); + } + return this.isOpen(); + }, + close: function close() { + if (this.isOpen() && !this.eventBus.before("close")) { + this.input.setAriaExpanded(false); + this.menu.close(); + this.input.clearHint(); + this.input.resetInputValue(); + this.eventBus.trigger("close"); + } + return !this.isOpen(); + }, + setVal: function setVal(val) { + this.input.setQuery(_.toStr(val)); + }, + getVal: function getVal() { + return this.input.getQuery(); + }, + select: function select($selectable) { + var data = this.menu.getSelectableData($selectable); + if (data && !this.eventBus.before("select", data.obj, data.dataset)) { + this.input.setQuery(data.val, true); + this.eventBus.trigger("select", data.obj, data.dataset); + this.close(); + return true; + } + return false; + }, + autocomplete: function autocomplete($selectable) { + var query, data, isValid; + query = this.input.getQuery(); + data = this.menu.getSelectableData($selectable); + isValid = data && query !== data.val; + if (isValid && !this.eventBus.before("autocomplete", data.obj, data.dataset)) { + this.input.setQuery(data.val); + this.eventBus.trigger("autocomplete", data.obj, data.dataset); + return true; + } + return false; + }, + moveCursor: function moveCursor(delta) { + var query, $candidate, data, suggestion, datasetName, cancelMove, id; + query = this.input.getQuery(); + $candidate = this.menu.selectableRelativeToCursor(delta); + data = this.menu.getSelectableData($candidate); + suggestion = data ? data.obj : null; + datasetName = data ? data.dataset : null; + id = $candidate ? $candidate.attr("id") : null; + this.input.trigger("cursorchange", id); + cancelMove = this._minLengthMet() && this.menu.update(query); + if (!cancelMove && !this.eventBus.before("cursorchange", suggestion, datasetName)) { + this.menu.setCursor($candidate); + if (data) { + if (typeof data.val === "string") { + this.input.setInputValue(data.val); + } + } else { + this.input.resetInputValue(); + this._updateHint(); + } + this.eventBus.trigger("cursorchange", suggestion, datasetName); + return true; + } + return false; + }, + destroy: function destroy() { + this.input.destroy(); + this.menu.destroy(); + } + }); + return Typeahead; + function c(ctx) { + var methods = [].slice.call(arguments, 1); + return function() { + var args = [].slice.call(arguments); + _.each(methods, function(method) { + return ctx[method].apply(ctx, args); + }); + }; + } + }(); + (function() { + "use strict"; + var old, keys, methods; + old = $.fn.typeahead; + keys = { + www: "tt-www", + attrs: "tt-attrs", + typeahead: "tt-typeahead" + }; + methods = { + initialize: function initialize(o, datasets) { + var www; + datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); + o = o || {}; + www = WWW(o.classNames); + return this.each(attach); + function attach() { + var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, status, typeahead, MenuConstructor; + _.each(datasets, function(d) { + d.highlight = !!o.highlight; + }); + $input = $(this); + $wrapper = $(www.html.wrapper); + $hint = $elOrNull(o.hint); + $menu = $elOrNull(o.menu); + defaultHint = o.hint !== false && !$hint; + defaultMenu = o.menu !== false && !$menu; + defaultHint && ($hint = buildHintFromInput($input, www)); + defaultMenu && ($menu = $(www.html.menu).css(www.css.menu)); + $hint && $hint.val(""); + $input = prepInput($input, www); + if (defaultHint || defaultMenu) { + $wrapper.css(www.css.wrapper); + $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint); + $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null); + } + MenuConstructor = defaultMenu ? DefaultMenu : Menu; + eventBus = new EventBus({ + el: $input + }); + input = new Input({ + hint: $hint, + input: $input, + menu: $menu + }, www); + menu = new MenuConstructor({ + node: $menu, + datasets: datasets + }, www); + status = new Status({ + $input: $input, + menu: menu + }); + typeahead = new Typeahead({ + input: input, + menu: menu, + eventBus: eventBus, + minLength: o.minLength, + autoselect: o.autoselect + }, www); + $input.data(keys.www, www); + $input.data(keys.typeahead, typeahead); + } + }, + isEnabled: function isEnabled() { + var enabled; + ttEach(this.first(), function(t) { + enabled = t.isEnabled(); + }); + return enabled; + }, + enable: function enable() { + ttEach(this, function(t) { + t.enable(); + }); + return this; + }, + disable: function disable() { + ttEach(this, function(t) { + t.disable(); + }); + return this; + }, + isActive: function isActive() { + var active; + ttEach(this.first(), function(t) { + active = t.isActive(); + }); + return active; + }, + activate: function activate() { + ttEach(this, function(t) { + t.activate(); + }); + return this; + }, + deactivate: function deactivate() { + ttEach(this, function(t) { + t.deactivate(); + }); + return this; + }, + isOpen: function isOpen() { + var open; + ttEach(this.first(), function(t) { + open = t.isOpen(); + }); + return open; + }, + open: function open() { + ttEach(this, function(t) { + t.open(); + }); + return this; + }, + close: function close() { + ttEach(this, function(t) { + t.close(); + }); + return this; + }, + select: function select(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.select($el); + }); + return success; + }, + autocomplete: function autocomplete(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.autocomplete($el); + }); + return success; + }, + moveCursor: function moveCursoe(delta) { + var success = false; + ttEach(this.first(), function(t) { + success = t.moveCursor(delta); + }); + return success; + }, + val: function val(newVal) { + var query; + if (!arguments.length) { + ttEach(this.first(), function(t) { + query = t.getVal(); + }); + return query; + } else { + ttEach(this, function(t) { + t.setVal(_.toStr(newVal)); + }); + return this; + } + }, + destroy: function destroy() { + ttEach(this, function(typeahead, $input) { + revert($input); + typeahead.destroy(); + }); + return this; + } + }; + $.fn.typeahead = function(method) { + if (methods[method]) { + return methods[method].apply(this, [].slice.call(arguments, 1)); + } else { + return methods.initialize.apply(this, arguments); + } + }; + $.fn.typeahead.noConflict = function noConflict() { + $.fn.typeahead = old; + return this; + }; + function ttEach($els, fn) { + $els.each(function() { + var $input = $(this), typeahead; + (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input); + }); + } + function buildHintFromInput($input, www) { + return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop({ + readonly: true, + required: false + }).removeAttr("id name placeholder").removeClass("required").attr({ + spellcheck: "false", + tabindex: -1 + }); + } + function prepInput($input, www) { + $input.data(keys.attrs, { + dir: $input.attr("dir"), + autocomplete: $input.attr("autocomplete"), + spellcheck: $input.attr("spellcheck"), + style: $input.attr("style") + }); + $input.addClass(www.classes.input).attr({ + spellcheck: false + }); + try { + !$input.attr("dir") && $input.attr("dir", "auto"); + } catch (e) {} + return $input; + } + function getBackgroundStyles($el) { + return { + backgroundAttachment: $el.css("background-attachment"), + backgroundClip: $el.css("background-clip"), + backgroundColor: $el.css("background-color"), + backgroundImage: $el.css("background-image"), + backgroundOrigin: $el.css("background-origin"), + backgroundPosition: $el.css("background-position"), + backgroundRepeat: $el.css("background-repeat"), + backgroundSize: $el.css("background-size") + }; + } + function revert($input) { + var www, $wrapper; + www = $input.data(keys.www); + $wrapper = $input.parent().filter(www.selectors.wrapper); + _.each($input.data(keys.attrs), function(val, key) { + _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); + }); + $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input); + if ($wrapper.length) { + $input.detach().insertAfter($wrapper); + $wrapper.remove(); + } + } + function $elOrNull(obj) { + var isValid, $el; + isValid = _.isJQuery(obj) || _.isElement(obj); + $el = isValid ? $(obj).first() : []; + return $el.length ? $el : null; + } + })(); +}); \ No newline at end of file diff --git a/docs/1.5.1/AsyncHTTPClient/search.json b/docs/1.5.1/AsyncHTTPClient/search.json new file mode 100644 index 000000000..fe97234c3 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/search.json @@ -0,0 +1 @@ +{"Structs/HTTPClientError.html#/s:s23CustomStringConvertibleP11descriptionSSvp":{"name":"description","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV10invalidURLACvpZ":{"name":"invalidURL","abstract":"

URL provided is invalid.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV9emptyHostACvpZ":{"name":"emptyHost","abstract":"

URL does not contain host.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV17missingSocketPathACvpZ":{"name":"missingSocketPath","abstract":"

URL does not contain a socketPath as a host for http(s)+unix shemes.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV15alreadyShutdownACvpZ":{"name":"alreadyShutdown","abstract":"

Client is shutdown and cannot be used for new requests.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV11emptySchemeACvpZ":{"name":"emptyScheme","abstract":"

URL does not contain scheme.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV17unsupportedSchemeyACSSFZ":{"name":"unsupportedScheme(_:)","abstract":"

Provided URL scheme is not supported, supported schemes are: http and https

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV11readTimeoutACvpZ":{"name":"readTimeout","abstract":"

Request timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV22remoteConnectionClosedACvpZ":{"name":"remoteConnectionClosed","abstract":"

Remote connection was closed unexpectedly.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV9cancelledACvpZ":{"name":"cancelled","abstract":"

Request was cancelled.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV32identityCodingIncorrectlyPresentACvpZ":{"name":"identityCodingIncorrectlyPresent","abstract":"

Request contains invalid identity encoding.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV29chunkedSpecifiedMultipleTimesACvpZ":{"name":"chunkedSpecifiedMultipleTimes","abstract":"

Request contains multiple chunks definitions.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20invalidProxyResponseACvpZ":{"name":"invalidProxyResponse","abstract":"

Proxy response was invalid.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20contentLengthMissingACvpZ":{"name":"contentLengthMissing","abstract":"

Request does not contain Content-Length header.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV27proxyAuthenticationRequiredACvpZ":{"name":"proxyAuthenticationRequired","abstract":"

Proxy Authentication Required.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20redirectLimitReachedACvpZ":{"name":"redirectLimitReached","abstract":"

Redirect Limit reached.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV21redirectCycleDetectedACvpZ":{"name":"redirectCycleDetected","abstract":"

Redirect Cycle detected.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV15uncleanShutdownACvpZ":{"name":"uncleanShutdown","abstract":"

Unclean shutdown.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20traceRequestWithBodyACvpZ":{"name":"traceRequestWithBody","abstract":"

A body was sent in a request with method TRACE.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV23invalidHeaderFieldNamesyACSaySSGFZ":{"name":"invalidHeaderFieldNames(_:)","abstract":"

Header field names contain invalid characters.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV18bodyLengthMismatchACvpZ":{"name":"bodyLengthMismatch","abstract":"

Body length is not equal to Content-Length.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV21writeAfterRequestSentACvpZ":{"name":"writeAfterRequestSent","abstract":"

Body part was written after request was fully sent.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV19incompatibleHeadersACvpZ":{"name":"incompatibleHeaders","abstract":"

Incompatible headers specified, for example Transfer-Encoding and Content-Length.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV14connectTimeoutACvpZ":{"name":"connectTimeout","abstract":"

Creating a new tcp connection timed out

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV21socksHandshakeTimeoutACvpZ":{"name":"socksHandshakeTimeout","abstract":"

The socks handshake timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV25httpProxyHandshakeTimeoutACvpZ":{"name":"httpProxyHandshakeTimeout","abstract":"

The http proxy connection creation timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV19tlsHandshakeTimeoutACvpZ":{"name":"tlsHandshakeTimeout","abstract":"

The tls handshake timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV43serverOfferedUnsupportedApplicationProtocolyACSSFZ":{"name":"serverOfferedUnsupportedApplicationProtocol(_:)","abstract":"

The remote server only offered an unsupported application protocol

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html":{"name":"HTTPClientError","abstract":"

Possible client errors.

"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP0C0Qa":{"name":"Response","abstract":"

Undocumented

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didSendRequestHead4task_yAA0B0C4TaskCy_0C0QzG_8NIOHTTP1011HTTPRequestH0VtF":{"name":"didSendRequestHead(task:_:)","abstract":"

Called when the request head is sent. Will be called once.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didSendRequestPart4task_yAA0B0C4TaskCy_0C0QzG_7NIOCore6IODataOtF":{"name":"didSendRequestPart(task:_:)","abstract":"

Called when a part of the request body is sent. Could be called zero or more times.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didSendRequest4taskyAA0B0C4TaskCy_0C0QzG_tF":{"name":"didSendRequest(task:)","abstract":"

Called when the request is fully sent. Will be called once.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","abstract":"

Called when response head is received. Will be called once.","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","abstract":"

Called when part of a response body is received. Could be called zero or more times.","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","abstract":"

Called when error was thrown during request execution. Will be called zero or one time only. Request processing will be stopped after that.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","abstract":"

Called when the complete HTTP request is finished. You must return an instance of your Response associated type. Will be called once, except if an error occurred.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html":{"name":"HTTPClientResponseDelegate","abstract":"

HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed."},"Extensions/URL.html#/s:10Foundation3URLV15AsyncHTTPClientE21httpURLWithSocketPath3uriACSgSS_SStcfc":{"name":"init(httpURLWithSocketPath:uri:)","abstract":"

Initializes a newly created HTTP URL connecting to a unix domain socket path. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “http+unix” scheme.

","parent_name":"URL"},"Extensions/URL.html#/s:10Foundation3URLV15AsyncHTTPClientE22httpsURLWithSocketPath3uriACSgSS_SStcfc":{"name":"init(httpsURLWithSocketPath:uri:)","abstract":"

Initializes a newly created HTTPS URL connecting to a unix domain socket path over TLS. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “https+unix” scheme.

","parent_name":"URL"},"Extensions/URL.html":{"name":"URL"},"Extensions.html#/HTTP1ConnectionProvider":{"name":"HTTP1ConnectionProvider"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B15CopyingDelegateC8Responsea":{"name":"Response","abstract":"

Undocumented

","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B15CopyingDelegateC12chunkHandlerAC7NIOCore15EventLoopFutureCyytGAE10ByteBufferVc_tcfc":{"name":"init(chunkHandler:)","abstract":"

Undocumented

","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"HTTPClientCopyingDelegate"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient19ResponseAccumulatorC0C0a":{"name":"Response","abstract":"

Undocumented

","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient19ResponseAccumulatorC7requestAcA0B0C7RequestV_tcfc":{"name":"init(request:)","abstract":"

Undocumented

","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"ResponseAccumulator"},"Classes/HTTPClient/NWTLSError.html#/status":{"name":"status","abstract":"

TLS error status. List of TLS errors can be found in

","parent_name":"NWTLSError"},"Classes/HTTPClient/NWTLSError.html#/init(_:reason:)":{"name":"init(_:reason:)","abstract":"

initialise a NWTLSError

","parent_name":"NWTLSError"},"Classes/HTTPClient/NWTLSError.html#/description":{"name":"description","parent_name":"NWTLSError"},"Classes/HTTPClient/NWPOSIXError.html#/errorCode":{"name":"errorCode","abstract":"

POSIX error code (enum)

","parent_name":"NWPOSIXError"},"Classes/HTTPClient/NWPOSIXError.html#/init(_:reason:)":{"name":"init(_:reason:)","abstract":"

Initialise a NWPOSIXError

","parent_name":"NWPOSIXError"},"Classes/HTTPClient/NWPOSIXError.html#/description":{"name":"description","parent_name":"NWPOSIXError"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC9eventLoop7NIOCore05EventE0_pvp":{"name":"eventLoop","abstract":"

The EventLoop the delegate will be executed on.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC12futureResult7NIOCore15EventLoopFutureCyxGvp":{"name":"futureResult","abstract":"

EventLoopFuture for the response returned by this request.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC4waitxyKF":{"name":"wait()","abstract":"

Waits for execution of this request to complete.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC6cancelyyF":{"name":"cancel()","abstract":"

Cancels the request execution.

","parent_name":"Task"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV5basic8username8passwordAESS_SStFZ":{"name":"basic(username:password:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV5basic11credentialsAESS_tFZ":{"name":"basic(credentials:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV6bearer6tokensAESS_tFZ":{"name":"bearer(tokens:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV11headerValueSSvp":{"name":"headerValue","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6method8NIOHTTP110HTTPMethodOvp":{"name":"method","abstract":"

Request HTTP method, defaults to GET.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url10Foundation3URLVvp":{"name":"url","abstract":"

Remote URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6schemeSSvp":{"name":"scheme","abstract":"

Remote HTTP scheme, resolved from URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4hostSSvp":{"name":"host","abstract":"

Remote host, resolved from URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV7headers8NIOHTTP111HTTPHeadersVvp":{"name":"headers","abstract":"

Request custom HTTP Headers, defaults to no headers.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4bodyAC4BodyVSgvp":{"name":"body","abstract":"

Request body, defaults to no body.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV16tlsConfiguration6NIOSSL16TLSConfigurationVSgvp":{"name":"tlsConfiguration","abstract":"

Request-specific TLS configuration, defaults to no request-specific TLS configuration.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4bodyAESS_8NIOHTTP110HTTPMethodOAJ11HTTPHeadersVAC4BodyVSgtKcfc":{"name":"init(url:method:headers:body:)","abstract":"

Create HTTP request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4body16tlsConfigurationAESS_8NIOHTTP110HTTPMethodOAK11HTTPHeadersVAC4BodyVSg6NIOSSL16TLSConfigurationVSgtKcfc":{"name":"init(url:method:headers:body:tlsConfiguration:)","abstract":"

Create HTTP request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4bodyAE10Foundation3URLV_8NIOHTTP110HTTPMethodOAM11HTTPHeadersVAC4BodyVSgtKcfc":{"name":"init(url:method:headers:body:)","abstract":"

Create an HTTP Request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4body16tlsConfigurationAE10Foundation3URLV_8NIOHTTP110HTTPMethodOAN11HTTPHeadersVAC4BodyVSg6NIOSSL16TLSConfigurationVSgtKcfc":{"name":"init(url:method:headers:body:tlsConfiguration:)","abstract":"

Create an HTTP Request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6useTLSSbvp":{"name":"useTLS","abstract":"

Whether request will be executed using secure socket.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4portSivp":{"name":"port","abstract":"

Resolved port.

","parent_name":"Request"},"Classes/HTTPClient/Body/StreamWriter.html#/s:15AsyncHTTPClient0B0C4BodyV12StreamWriterV7closureAG7NIOCore15EventLoopFutureCyytGAI6IODataOc_tcfc":{"name":"init(closure:)","abstract":"

Create new StreamWriter

","parent_name":"StreamWriter"},"Classes/HTTPClient/Body/StreamWriter.html#/s:15AsyncHTTPClient0B0C4BodyV12StreamWriterV5writey7NIOCore15EventLoopFutureCyytGAI6IODataOF":{"name":"write(_:)","abstract":"

Write data to server.

","parent_name":"StreamWriter"},"Classes/HTTPClient/Body/StreamWriter.html":{"name":"StreamWriter","abstract":"

Chunk provider.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6lengthSiSgvp":{"name":"length","abstract":"

Body size. Request validation will be failed with HTTPClientErrors.contentLengthMissing if nil,","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6streamy7NIOCore15EventLoopFutureCyytGAE12StreamWriterVcvp":{"name":"stream","abstract":"

Body chunk provider.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV10byteBufferyAE7NIOCore04ByteE0VFZ":{"name":"byteBuffer(_:)","abstract":"

Create and stream body using ByteBuffer.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6stream6length_AESiSg_7NIOCore15EventLoopFutureCyytGAE12StreamWriterVctFZ":{"name":"stream(length:_:)","abstract":"

Create and stream body using StreamWriter.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV4datayAE10Foundation4DataVFZ":{"name":"data(_:)","abstract":"

Create and stream body using Data.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6stringyAESSFZ":{"name":"string(_:)","abstract":"

Create and stream body using String.

","parent_name":"Body"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4hostSSvp":{"name":"host","abstract":"

Remote host of the request.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV6status8NIOHTTP118HTTPResponseStatusOvp":{"name":"status","abstract":"

Response HTTP status.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7version8NIOHTTP111HTTPVersionVvp":{"name":"version","abstract":"

Response HTTP version.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7headers8NIOHTTP111HTTPHeadersVvp":{"name":"headers","abstract":"

Reponse HTTP headers.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4body7NIOCore10ByteBufferVSgvp":{"name":"body","abstract":"

Response body.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4host6status7headers4bodyAESS_8NIOHTTP118HTTPResponseStatusOAJ11HTTPHeadersV7NIOCore10ByteBufferVSgtcfc":{"name":"init(host:status:headers:body:)","abstract":"

Create HTTP Response.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4host6status7version7headers4bodyAESS_8NIOHTTP118HTTPResponseStatusOAK11HTTPVersionVAK11HTTPHeadersV7NIOCore10ByteBufferVSgtcfc":{"name":"init(host:status:version:headers:body:)","abstract":"

Create HTTP Response.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7cookiesSayAC6CookieVGvp":{"name":"cookies","abstract":"

List of HTTP cookies returned by the server.

","parent_name":"Response"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4nameSSvp":{"name":"name","abstract":"

The name of the cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV5valueSSvp":{"name":"value","abstract":"

The cookie’s string value.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4pathSSvp":{"name":"path","abstract":"

The cookie’s path.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6domainSSSgvp":{"name":"domain","abstract":"

The domain of the cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV7expires10Foundation4DateVSgvp":{"name":"expires","abstract":"

The cookie’s expiration date.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6maxAgeSiSgvp":{"name":"maxAge","abstract":"

The cookie’s age in seconds.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV8httpOnlySbvp":{"name":"httpOnly","abstract":"

Whether the cookie should only be sent to HTTP servers.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6secureSbvp":{"name":"secure","abstract":"

Whether the cookie should only be sent over secure channels.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6header13defaultDomainAESgSS_SStcfc":{"name":"init(header:defaultDomain:)","abstract":"

Create a Cookie by parsing a Set-Cookie header.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4name5value4path6domain7expires6maxAge8httpOnly6secureAESS_S3SSg10Foundation4DateVSgSiSgS2btcfc":{"name":"init(name:value:path:domain:expires:maxAge:httpOnly:secure:)","abstract":"

Create HTTP cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Decompression.html#/s:15AsyncHTTPClient0B0C13DecompressionO8disabledyA2EmF":{"name":"disabled","abstract":"

Decompression is disabled.

","parent_name":"Decompression"},"Classes/HTTPClient/Decompression.html#/s:15AsyncHTTPClient0B0C13DecompressionO7enabledyAE18NIOHTTPCompression20NIOHTTPDecompressionO0C5LimitV_tcAEmF":{"name":"enabled(limit:)","abstract":"

Decompression is enabled.

","parent_name":"Decompression"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV11indifferentAEvpZ":{"name":"indifferent","abstract":"

Event Loop will be selected by the library.

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV8delegate2onAE7NIOCore0cD0_p_tFZ":{"name":"delegate(on:)","abstract":"

The delegate will be run on the specified EventLoop (and the Channel if possible).

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV18delegateAndChannel2onAE7NIOCore0cD0_p_tFZ":{"name":"delegateAndChannel(on:)","abstract":"

The delegate and the Channel will be run on the specified EventLoop.

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:s23CustomStringConvertibleP11descriptionSSvp":{"name":"description","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopGroupProvider.html#/s:15AsyncHTTPClient0B0C22EventLoopGroupProviderO6sharedyAE7NIOCore0cdE0_pcAEmF":{"name":"shared(_:)","abstract":"

EventLoopGroup will be provided by the user. Owner of this group is responsible for its lifecycle.

","parent_name":"EventLoopGroupProvider"},"Classes/HTTPClient/EventLoopGroupProvider.html#/s:15AsyncHTTPClient0B0C22EventLoopGroupProviderO9createNewyA2EmF":{"name":"createNew","abstract":"

EventLoopGroup will be created by the client. When syncShutdown is called, created EventLoopGroup will be shut down as well.

","parent_name":"EventLoopGroupProvider"},"Classes/HTTPClient/Configuration/ConnectionPool.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14ConnectionPoolV11idleTimeout7NIOCore10TimeAmountVvp":{"name":"idleTimeout","abstract":"

Undocumented

","parent_name":"ConnectionPool"},"Classes/HTTPClient/Configuration/ConnectionPool.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14ConnectionPoolV11idleTimeoutAG7NIOCore10TimeAmountV_tcfc":{"name":"init(idleTimeout:)","abstract":"

Undocumented

","parent_name":"ConnectionPool"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08RedirectC0V8disallowAGvpZ":{"name":"disallow","abstract":"

Redirects are not followed.

","parent_name":"RedirectConfiguration"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08RedirectC0V6follow3max11allowCyclesAGSi_SbtFZ":{"name":"follow(max:allowCycles:)","abstract":"

Redirects are followed with a specified limit.

","parent_name":"RedirectConfiguration"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV7connect7NIOCore10TimeAmountVSgvp":{"name":"connect","abstract":"

Specifies connect timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV4read7NIOCore10TimeAmountVSgvp":{"name":"read","abstract":"

Specifies read timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV7connect4readAG7NIOCore10TimeAmountVSg_AMtcfc":{"name":"init(connect:read:)","abstract":"

Create timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV4hostSSvp":{"name":"host","abstract":"

Specifies Proxy server host.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV4portSivp":{"name":"port","abstract":"

Specifies Proxy server port.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV13authorizationAC13AuthorizationVSgvp":{"name":"authorization","abstract":"

Specifies Proxy server authorization.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV6server4host4portAGSS_SitFZ":{"name":"server(host:port:)","abstract":"

Create a HTTP proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV6server4host4port13authorizationAGSS_SiAC13AuthorizationVSgtFZ":{"name":"server(host:port:authorization:)","abstract":"

Create a HTTP proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV11socksServer4host4portAGSS_SitFZ":{"name":"socksServer(host:port:)","abstract":"

Create a SOCKSv5 proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC06NIOSSL16TLSConfigurationVSgvp":{"name":"tlsConfiguration","abstract":"

TLS configuration, defaults to TLSConfiguration.makeClientConfiguration().

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08redirectC0AE08RedirectC0Vvp":{"name":"redirectConfiguration","abstract":"

Enables following 3xx redirects automatically, defaults to RedirectConfiguration().

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7timeoutAE7TimeoutVvp":{"name":"timeout","abstract":"

Default client timeout, defaults to no read timeout and 10 seconds connect timeout.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14connectionPoolAE010ConnectionE0Vvp":{"name":"connectionPool","abstract":"

Connection pool configuration.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5proxyAE5ProxyVSgvp":{"name":"proxy","abstract":"

Upstream proxy, defaults to no proxy.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV13decompressionAC13DecompressionOvp":{"name":"decompression","abstract":"

Enables automatic body decompression. Supported algorithms are gzip and deflate.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV24ignoreUncleanSSLShutdownSbvp":{"name":"ignoreUncleanSSLShutdown","abstract":"

Ignore TLS unclean shutdown error, defaults to false.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC008redirectC07timeout14connectionPool5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL16TLSConfigurationVSg_AE08RedirectC0VSgAE7TimeoutVAE010ConnectionH0VAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(tlsConfiguration:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC008redirectC07timeout5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL16TLSConfigurationVSg_AE08RedirectC0VSgAE7TimeoutVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(tlsConfiguration:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout38maximumAllowedIdleTimeInConnectionPool5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutV7NIOCore0K6AmountVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:maximumAllowedIdleTimeInConnectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout14connectionPool5proxy24ignoreUncleanSSLShutdown13decompression24backgroundActivityLoggerAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutV7NIOCore10TimeAmountVAE5ProxyVSgSbAC13DecompressionO7Logging0Q0VSgtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:backgroundActivityLogger:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/Proxy.html":{"name":"Proxy","abstract":"

Proxy server configuration","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/Timeout.html":{"name":"Timeout","abstract":"

Timeout configuration.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html":{"name":"RedirectConfiguration","abstract":"

Specifies redirect processing settings.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/ConnectionPool.html":{"name":"ConnectionPool","abstract":"

Connection pool configuration.

","parent_name":"Configuration"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C14eventLoopGroup7NIOCore05EventdE0_pvp":{"name":"eventLoopGroup","abstract":"

Undocumented

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C22eventLoopGroupProvider13configurationA2C05EventdeF0O_AC13ConfigurationVtcfc":{"name":"init(eventLoopGroupProvider:configuration:)","abstract":"

Create an HTTPClient with specified EventLoopGroup provider and configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C22eventLoopGroupProvider13configuration24backgroundActivityLoggerA2C05EventdeF0O_AC13ConfigurationV7Logging0J0Vtcfc":{"name":"init(eventLoopGroupProvider:configuration:backgroundActivityLogger:)","abstract":"

Create an HTTPClient with specified EventLoopGroup provider and configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C12syncShutdownyyKF":{"name":"syncShutdown()","abstract":"

Shuts down the client and EventLoopGroup if it was created by the client.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C8shutdown5queue_y8Dispatch0E5QueueC_ys5Error_pSgctF":{"name":"shutdown(queue:_:)","abstract":"

Shuts down the client and event loop gracefully. This function is clearly an outlier in that it uses a completion","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3get3url8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AG11NIODeadlineVSgtF":{"name":"get(url:deadline:)","abstract":"

Execute GET request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3get3url8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"get(url:deadline:logger:)","abstract":"

Execute GET request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C4post3url4body8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"post(url:body:deadline:)","abstract":"

Execute POST request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C4post3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"post(url:body:deadline:logger:)","abstract":"

Execute POST request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C5patch3url4body8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"patch(url:body:deadline:)","abstract":"

Execute PATCH request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C5patch3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"patch(url:body:deadline:logger:)","abstract":"

Execute PATCH request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3put3url4body8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"put(url:body:deadline:)","abstract":"

Execute PUT request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3put3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"put(url:body:deadline:logger:)","abstract":"

Execute PUT request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C6delete3url8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AG11NIODeadlineVSgtF":{"name":"delete(url:deadline:)","abstract":"

Execute DELETE request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C6delete3url8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"delete(url:deadline:logger:)","abstract":"

Execute DELETE request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_SSAC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:url:body:deadline:logger:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_10socketPath03urlE04body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_S2SAC4BodyVSgAJ11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:socketPath:urlPath:body:deadline:logger:)","abstract":"

Execute arbitrary HTTP+UNIX request to a unix domain socket path, using the specified URL as the request to send to the server.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_16secureSocketPath03urlF04body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_S2SAC4BodyVSgAJ11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:secureSocketPath:urlPath:body:deadline:logger:)","abstract":"

Execute arbitrary HTTPS+UNIX request to a unix domain socket path over TLS, using the specified URL as the request to send to the server.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGAC7RequestV_AG11NIODeadlineVSgtF":{"name":"execute(request:deadline:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGAC7RequestV_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"execute(request:deadline:logger:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request9eventLoop8deadline7NIOCore05EventF6FutureCyAC8ResponseVGAC7RequestV_AC0iF10PreferenceVAH11NIODeadlineVSgtF":{"name":"execute(request:eventLoop:deadline:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request9eventLoop8deadline6logger7NIOCore05EventF6FutureCyAC8ResponseVGAC7RequestV_AC0jF10PreferenceVAI11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(request:eventLoop:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate8deadlineAC4TaskCy_8ResponseQzGAC7RequestV_x7NIOCore11NIODeadlineVSgtAA0bH8DelegateRzlF":{"name":"execute(request:delegate:deadline:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate8deadline6loggerAC4TaskCy_8ResponseQzGAC7RequestV_x7NIOCore11NIODeadlineVSg7Logging6LoggerVtAA0bI8DelegateRzlF":{"name":"execute(request:delegate:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate9eventLoop8deadlineAC4TaskCy_8ResponseQzGAC7RequestV_xAC05EventG10PreferenceV7NIOCore11NIODeadlineVSgtAA0bJ8DelegateRzlF":{"name":"execute(request:delegate:eventLoop:deadline:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate9eventLoop8deadline6loggerAC4TaskCy_8ResponseQzGAC7RequestV_xAC05EventG10PreferenceV7NIOCore11NIODeadlineVSg7Logging6LoggerVSgtAA0bK8DelegateRzlF":{"name":"execute(request:delegate:eventLoop:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Configuration.html":{"name":"Configuration","abstract":"

HTTPClient configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient/EventLoopGroupProvider.html":{"name":"EventLoopGroupProvider","abstract":"

Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

","parent_name":"HTTPClient"},"Classes/HTTPClient/EventLoopPreference.html":{"name":"EventLoopPreference","abstract":"

Specifies how the library will treat event loop passed by the user.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Decompression.html":{"name":"Decompression","abstract":"

Specifies decompression settings.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Cookie.html":{"name":"Cookie","abstract":"

A representation of an HTTP cookie.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Response.html":{"name":"Response","abstract":"

Represent HTTP response.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Body.html":{"name":"Body","abstract":"

Represent request body.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Request.html":{"name":"Request","abstract":"

Represent HTTP request.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Authorization.html":{"name":"Authorization","abstract":"

HTTP authentication

","parent_name":"HTTPClient"},"Classes/HTTPClient/Task.html":{"name":"Task","abstract":"

Response execution context. Will be created by the library and could be used for obtaining","parent_name":"HTTPClient"},"Classes/HTTPClient/NWPOSIXError.html":{"name":"NWPOSIXError","parent_name":"HTTPClient"},"Classes/HTTPClient/NWTLSError.html":{"name":"NWTLSError","parent_name":"HTTPClient"},"Classes/FileDownloadDelegate/Progress.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8ProgressV10totalBytesSiSgvp":{"name":"totalBytes","abstract":"

Undocumented

","parent_name":"Progress"},"Classes/FileDownloadDelegate/Progress.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8ProgressV13receivedBytesSivp":{"name":"receivedBytes","abstract":"

Undocumented

","parent_name":"Progress"},"Classes/FileDownloadDelegate/Progress.html":{"name":"Progress","abstract":"

The response type for this delegate: the total count of bytes as reported by the response","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8Responsea":{"name":"Response","abstract":"

Undocumented

","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient20FileDownloadDelegateC4path4pool10reportHead0H8ProgressACSS_8NIOPosix13NIOThreadPoolCy8NIOHTTP1012HTTPResponseI0VcSgyAC0J0VcSgtKcfc":{"name":"init(path:pool:reportHead:reportProgress:)","abstract":"

Initializes a new file download delegate.

","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html":{"name":"FileDownloadDelegate","abstract":"

Handles a streaming download to a given file path, allowing headers and progress to be reported.

"},"Classes/HTTPClient.html":{"name":"HTTPClient","abstract":"

HTTPClient class provides API for request execution.

"},"Classes/ResponseAccumulator.html":{"name":"ResponseAccumulator","abstract":"

Undocumented

"},"Classes/HTTPClientCopyingDelegate.html":{"name":"HTTPClientCopyingDelegate","abstract":"

Undocumented

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Extensions.html":{"name":"Extensions","abstract":"

The following extensions are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"},"Structs.html":{"name":"Structures","abstract":"

The following structures are available globally.

"}} \ No newline at end of file diff --git a/docs/1.5.1/AsyncHTTPClient/undocumented.json b/docs/1.5.1/AsyncHTTPClient/undocumented.json new file mode 100644 index 000000000..2fd81ab68 --- /dev/null +++ b/docs/1.5.1/AsyncHTTPClient/undocumented.json @@ -0,0 +1,159 @@ +{ + "warnings": [ + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 23, + "symbol": "FileDownloadDelegate.Progress.totalBytes", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 24, + "symbol": "FileDownloadDelegate.Progress.receivedBytes", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 29, + "symbol": "FileDownloadDelegate.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 67, + "symbol": "HTTPClient.eventLoopGroup", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 664, + "symbol": "HTTPClient.Configuration.init(tlsConfiguration:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 680, + "symbol": "HTTPClient.Configuration.init(tlsConfiguration:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 697, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:maximumAllowedIdleTimeInConnectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 715, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:backgroundActivityLogger:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 734, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 888, + "symbol": "HTTPClient.Configuration.ConnectionPool.idleTimeout", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 890, + "symbol": "HTTPClient.Configuration.ConnectionPool.init(idleTimeout:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 357, + "symbol": "HTTPClient.Authorization.basic(username:password:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 361, + "symbol": "HTTPClient.Authorization.basic(credentials:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 365, + "symbol": "HTTPClient.Authorization.bearer(tokens:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 369, + "symbol": "HTTPClient.Authorization.headerValue", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 380, + "symbol": "ResponseAccumulator", + "symbol_kind": "source.lang.swift.decl.class", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 381, + "symbol": "ResponseAccumulator.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 394, + "symbol": "ResponseAccumulator.init(request:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 487, + "symbol": "HTTPClientResponseDelegate.Response", + "symbol_kind": "source.lang.swift.decl.associatedtype", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 26, + "symbol": "HTTPClientCopyingDelegate", + "symbol_kind": "source.lang.swift.decl.class", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 27, + "symbol": "HTTPClientCopyingDelegate.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 31, + "symbol": "HTTPClientCopyingDelegate.init(chunkHandler:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + } + ], + "source_directory": "/code" +} \ No newline at end of file diff --git a/docs/1.8.1/AsyncHTTPClient/Classes.html b/docs/1.8.1/AsyncHTTPClient/Classes.html new file mode 100644 index 000000000..3ff88b73c --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes.html @@ -0,0 +1,311 @@ + + + + Classes Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Classes

+

The following classes are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + FileDownloadDelegate + +
    +
    +
    +
    +
    +
    +

    Handles a streaming download to a given file path, allowing headers and progress to be reported.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class FileDownloadDelegate : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + HTTPClient + +
    +
    +
    +
    +
    +
    +

    HTTPClient class provides API for request execution.

    + +

    Example:

    +
        let client = HTTPClient(eventLoopGroupProvider: .createNew)
    +    client.get(url: "https://swift.org/", deadline: .now() + .seconds(1)).whenComplete { result in
    +        switch result {
    +        case .failure(let error):
    +            // process error
    +        case .success(let response):
    +            if let response.status == .ok {
    +                // handle response
    +            } else {
    +                // handle remote error
    +            }
    +        }
    +    }
    +
    + +

    It is important to close the client instance, for example in a defer statement, after use to cleanly shutdown the underlying NIO EventLoopGroup:

    +
        try client.syncShutdown()
    +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class HTTPClient
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + ResponseAccumulator + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class ResponseAccumulator : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Undocumented

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class HTTPClientCopyingDelegate : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/FileDownloadDelegate.html b/docs/1.8.1/AsyncHTTPClient/Classes/FileDownloadDelegate.html new file mode 100644 index 000000000..789045a03 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/FileDownloadDelegate.html @@ -0,0 +1,454 @@ + + + + FileDownloadDelegate Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

FileDownloadDelegate

+
+
+ +
public final class FileDownloadDelegate : HTTPClientResponseDelegate
+ +
+
+

Handles a streaming download to a given file path, allowing headers and progress to be reported.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + Progress + +
    +
    +
    +
    +
    +
    +

    The response type for this delegate: the total count of bytes as reported by the response +“Content-Length” header (if available) and the count of bytes downloaded.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Progress
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public typealias Response = Progress
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Initializes a new file download delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(
    +    path: String,
    +    pool: NIOThreadPool = NIOThreadPool(numberOfThreads: 1),
    +    reportHead: ((HTTPResponseHead) -> Void)? = nil,
    +    reportProgress: ((Progress) -> Void)? = nil
    +) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + path + + +
    +

    Path to a file you’d like to write the download to.

    +
    +
    + + pool + + +
    +

    A thread pool to use for asynchronous file I/O.

    +
    +
    + + reportHead + + +
    +

    A closure called when the response head is available.

    +
    +
    + + reportProgress + + +
    +

    A closure called when a body chunk has been downloaded, with +the total byte count and download byte count passed to it as arguments. The callbacks +will be invoked in the same threading context that the delegate itself is invoked, +as controlled by EventLoopPreference.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveHead(
    +    task: HTTPClient.Task<Response>,
    +    _ head: HTTPResponseHead
    +) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveBodyPart(
    +    task: HTTPClient.Task<Response>,
    +    _ buffer: ByteBuffer
    +) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveError(task: HTTPClient.Task<Progress>, _ error: Error)
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didFinishRequest(task: HTTPClient.Task<Response>) throws -> Response
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html b/docs/1.8.1/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html new file mode 100644 index 000000000..5d2de9579 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html @@ -0,0 +1,238 @@ + + + + Progress Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Progress

+
+
+ +
public struct Progress
+ +
+
+

The response type for this delegate: the total count of bytes as reported by the response +“Content-Length” header (if available) and the count of bytes downloaded.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + totalBytes + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var totalBytes: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + receivedBytes + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var receivedBytes: Int
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient.html b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient.html new file mode 100644 index 000000000..f45b31d37 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient.html @@ -0,0 +1,2483 @@ + + + + HTTPClient Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClient

+
+
+ +
public class HTTPClient
+ +
+
+

HTTPClient class provides API for request execution.

+ +

Example:

+
    let client = HTTPClient(eventLoopGroupProvider: .createNew)
+    client.get(url: "https://swift.org/", deadline: .now() + .seconds(1)).whenComplete { result in
+        switch result {
+        case .failure(let error):
+            // process error
+        case .success(let response):
+            if let response.status == .ok {
+                // handle response
+            } else {
+                // handle remote error
+            }
+        }
+    }
+
+ +

It is important to close the client instance, for example in a defer statement, after use to cleanly shutdown the underlying NIO EventLoopGroup:

+
    try client.syncShutdown()
+
+ + +
+
+ +
+
+
+
    +
  • +
    + + + + eventLoopGroup + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let eventLoopGroup: EventLoopGroup
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTPClient with specified EventLoopGroup provider and configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public convenience init(eventLoopGroupProvider: EventLoopGroupProvider,
    +                        configuration: Configuration = Configuration())
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + eventLoopGroupProvider + + +
    +

    Specify how EventLoopGroup will be created.

    +
    +
    + + configuration + + +
    +

    Client configuration.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTPClient with specified EventLoopGroup provider and configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public required init(eventLoopGroupProvider: EventLoopGroupProvider,
    +                     configuration: Configuration = Configuration(),
    +                     backgroundActivityLogger: Logger)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + eventLoopGroupProvider + + +
    +

    Specify how EventLoopGroup will be created.

    +
    +
    + + configuration + + +
    +

    Client configuration.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + syncShutdown() + +
    +
    +
    +
    +
    +
    +

    Shuts down the client and EventLoopGroup if it was created by the client.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func syncShutdown() throws
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + shutdown(queue:_:) + +
    +
    +
    +
    +
    +
    +

    Shuts down the client and event loop gracefully. This function is clearly an outlier in that it uses a completion +callback instead of an EventLoopFuture. The reason for that is that NIO’s EventLoopFutures will call back on an event loop. +The virtue of this function is to shut the event loop down. To work around that we call back on a DispatchQueue +instead.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func shutdown(queue: DispatchQueue = .global(), _ callback: @escaping (Error?) -> Void)
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + get(url:deadline:) + +
    +
    +
    +
    +
    +
    +

    Execute GET request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func get(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute GET request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func get(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute POST request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute POST request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PATCH request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PATCH request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PUT request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PUT request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + delete(url:deadline:) + +
    +
    +
    +
    +
    +
    +

    Execute DELETE request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func delete(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    The time when the request must have been completed by.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute DELETE request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func delete(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    The time when the request must have been completed by.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + url + + +
    +

    Request url.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP+UNIX request to a unix domain socket path, using the specified URL as the request to send to the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, socketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + urlPath + + +
    +

    The URL path and query that will be sent to the server.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTPS+UNIX request to a unix domain socket path over TLS, using the specified URL as the request to send to the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, secureSocketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + secureSocketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + urlPath + + +
    +

    The URL path and query that will be sent to the server.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, eventLoop: EventLoopPreference, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request,
    +                    eventLoop eventLoopPreference: EventLoopPreference,
    +                    deadline: NIODeadline? = nil,
    +                    logger: Logger?) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          deadline: NIODeadline? = nil) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          deadline: NIODeadline? = nil,
    +                                                          logger: Logger) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          eventLoop eventLoopPreference: EventLoopPreference,
    +                                                          deadline: NIODeadline? = nil) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(
    +    request: Request,
    +    delegate: Delegate,
    +    eventLoop eventLoopPreference: EventLoopPreference,
    +    deadline: NIODeadline? = nil,
    +    logger originalLogger: Logger?
    +) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + Configuration + +
    +
    +
    +
    +
    +
    +

    HTTPClient configuration.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Configuration
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum EventLoopGroupProvider
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + EventLoopPreference + +
    +
    +
    +
    +
    +
    +

    Specifies how the library will treat event loop passed by the user.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct EventLoopPreference
    +
    extension HTTPClient.EventLoopPreference: CustomStringConvertible
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Decompression + +
    +
    +
    +
    +
    +
    +

    Specifies decompression settings.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum Decompression
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Cookie + +
    +
    +
    +
    +
    +
    +

    A representation of an HTTP cookie.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Cookie
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Represent HTTP response.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Response
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Body + +
    +
    +
    +
    +
    +
    +

    Represent request body.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Body
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Request + +
    +
    +
    +
    +
    +
    +

    Represent HTTP request.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Request
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Authorization + +
    +
    +
    +
    +
    +
    +

    HTTP authentication

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Authorization : Hashable
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Task + +
    +
    +
    +
    +
    +
    +

    Response execution context. Will be created by the library and could be used for obtaining +EventLoopFuture<Response> of the execution or cancellation of the execution.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class Task<Response>
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + NWPOSIXError + +
    +
    +
    +
    +
    +
    + + See more +
    +
    +
    +
  • +
  • +
    + + + + NWTLSError + +
    +
    +
    +
    +
    +
    + + See more +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Authorization.html b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Authorization.html new file mode 100644 index 000000000..e1b547bcc --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Authorization.html @@ -0,0 +1,297 @@ + + + + Authorization Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Authorization

+
+
+ +
public struct Authorization : Hashable
+ +
+
+

HTTP authentication

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Body.html b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Body.html new file mode 100644 index 000000000..9e4f8027c --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Body.html @@ -0,0 +1,478 @@ + + + + Body Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Body

+
+
+ +
public struct Body
+ +
+
+

Represent request body.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + StreamWriter + +
    +
    +
    +
    +
    +
    +

    Chunk provider.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct StreamWriter
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + length + +
    +
    +
    +
    +
    +
    +

    Body size. if nil,Transfer-Encoding will automatically be set to chunked. Otherwise a Content-Length +header is set with the given length.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var length: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + stream + +
    +
    +
    +
    +
    +
    +

    Body chunk provider.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var stream: (StreamWriter) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + byteBuffer(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using ByteBuffer.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func byteBuffer(_ buffer: ByteBuffer) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + buffer + + +
    +

    Body ByteBuffer representation.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + stream(length:_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using StreamWriter.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func stream(length: Int? = nil, _ stream: @escaping (StreamWriter) -> EventLoopFuture<Void>) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + length + + +
    +

    Body size. If nil, Transfer-Encoding will automatically be set to chunked. Otherwise a Content-Length +header is set with the given length.

    +
    +
    + + stream + + +
    +

    Body chunk provider.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + data(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using Data.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func data(_ data: Data) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    Body Data representation.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + string(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using String.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func string(_ string: String) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + string + + +
    +

    Body String representation.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html new file mode 100644 index 000000000..65b7d5664 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html @@ -0,0 +1,275 @@ + + + + StreamWriter Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

StreamWriter

+
+
+ +
public struct StreamWriter
+ +
+
+

Chunk provider.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + init(closure:) + +
    +
    +
    +
    +
    +
    +

    Create new StreamWriter

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(closure: @escaping (IOData) -> EventLoopFuture<Void>)
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + closure + + +
    +

    function that will be called to write actual bytes to the channel.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + write(_:) + +
    +
    +
    +
    +
    +
    +

    Write data to server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func write(_ data: IOData) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    IOData to write.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Configuration.html b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Configuration.html new file mode 100644 index 000000000..2e7d4ac42 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Configuration.html @@ -0,0 +1,775 @@ + + + + Configuration Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Configuration

+
+
+ +
public struct Configuration
+ +
+
+

HTTPClient configuration.

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html new file mode 100644 index 000000000..af057eca1 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html @@ -0,0 +1,299 @@ + + + + ConnectionPool Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

ConnectionPool

+
+
+ +
public struct ConnectionPool : Hashable
+ +
+
+

Connection pool configuration.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + idleTimeout + +
    +
    +
    +
    +
    +
    +

    Specifies amount of time connections are kept idle in the pool. After this time has passed without a new +request the connections are closed.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var idleTimeout: TimeAmount
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The maximum number of connections that are kept alive in the connection pool per host. If requests with +an explicit eventLoopRequirement are sent, this number might be exceeded due to overflow connections.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var concurrentHTTP1ConnectionsPerHostSoftLimit: Int
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + init(idleTimeout:) + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(idleTimeout: TimeAmount = .seconds(60))
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(idleTimeout: TimeAmount, concurrentHTTP1ConnectionsPerHostSoftLimit: Int)
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/HTTPVersion.html b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/HTTPVersion.html new file mode 100644 index 000000000..afd41eb98 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/HTTPVersion.html @@ -0,0 +1,237 @@ + + + + HTTPVersion Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPVersion

+
+
+ +
public struct HTTPVersion
+ +
+
+

Undocumented

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + http1Only + +
    +
    +
    +
    +
    +
    +

    we only use HTTP/1, even if the server would supports HTTP/2

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let http1Only: `Self`
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + automatic + +
    +
    +
    +
    +
    +
    +

    HTTP/2 is used if we connect to a server with HTTPS and the server supports HTTP/2, otherwise we use HTTP/1

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let automatic: `Self`
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html new file mode 100644 index 000000000..c603a204d --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html @@ -0,0 +1,475 @@ + + + + Proxy Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Proxy

+
+
+ +
public struct Proxy
+ +
+
+

Proxy server configuration +Specifies the remote address of an HTTP proxy.

+ +

Adding an Proxy to your client’s HTTPClient.Configuration +will cause requests to be passed through the specified proxy using the +HTTP CONNECT method.

+ +

If a TLSConfiguration is used in conjunction with HTTPClient.Configuration.Proxy, +TLS will be established after successful proxy, between your client +and the destination server.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server host.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + port + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server port.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var port: Int
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + authorization + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server authorization.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var authorization: HTTPClient.Authorization? { get set }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + server(host:port:) + +
    +
    +
    +
    +
    +
    +

    Create a HTTP proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func server(host: String, port: Int) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + host + + +
    +

    proxy server host.

    +
    +
    + + port + + +
    +

    proxy server port.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a HTTP proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func server(host: String, port: Int, authorization: HTTPClient.Authorization? = nil) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + host + + +
    +

    proxy server host.

    +
    +
    + + port + + +
    +

    proxy server port.

    +
    +
    + + authorization + + +
    +

    proxy server authorization.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a SOCKSv5 proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func socksServer(host: String, port: Int = 1080) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + host + + +
    +

    The SOCKSv5 proxy address.

    +
    +
    + + port + + +
    +

    The SOCKSv5 proxy port, defaults to 1080.

    +
    +
    +
    +
    +

    Return Value

    +

    A new instance of Proxy configured to connect to a SOCKSv5 server.

    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html new file mode 100644 index 000000000..c9260efdd --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html @@ -0,0 +1,273 @@ + + + + RedirectConfiguration Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

RedirectConfiguration

+
+
+ +
public struct RedirectConfiguration
+ +
+
+

Specifies redirect processing settings.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + disallow + +
    +
    +
    +
    +
    +
    +

    Redirects are not followed.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let disallow: HTTPClient.Configuration.RedirectConfiguration
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Redirects are followed with a specified limit.

    +
    +

    Warning

    +

    Cycle detection will keep all visited URLs in memory which means a malicious server could use this as a denial-of-service vector.

    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func follow(max: Int, allowCycles: Bool) -> RedirectConfiguration
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + max + + +
    +

    The maximum number of allowed redirects.

    +
    +
    + + allowCycles + + +
    +

    Whether cycles are allowed.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html new file mode 100644 index 000000000..b7aeb478f --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html @@ -0,0 +1,299 @@ + + + + Timeout Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Timeout

+
+
+ +
public struct Timeout
+ +
+
+

Timeout configuration.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + connect + +
    +
    +
    +
    +
    +
    +

    Specifies connect timeout. If no connect timeout is given, a default 30 seconds timeout will applied.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var connect: TimeAmount?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + read + +
    +
    +
    +
    +
    +
    +

    Specifies read timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var read: TimeAmount?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + init(connect:read:) + +
    +
    +
    +
    +
    +
    +

    Create timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(connect: TimeAmount? = nil, read: TimeAmount? = nil)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + connect + + +
    +

    connect timeout. Will default to 10 seconds, if no value is +provided. See var connectionCreationTimeout

    +
    +
    + + read + + +
    +

    read timeout.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Cookie.html b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Cookie.html new file mode 100644 index 000000000..3c899cecc --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Cookie.html @@ -0,0 +1,615 @@ + + + + Cookie Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Cookie

+
+
+ +
public struct Cookie
+ +
+
+

A representation of an HTTP cookie.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + name + +
    +
    +
    +
    +
    +
    +

    The name of the cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var name: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + value + +
    +
    +
    +
    +
    +
    +

    The cookie’s string value.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var value: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + path + +
    +
    +
    +
    +
    +
    +

    The cookie’s path.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var path: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + domain + +
    +
    +
    +
    +
    +
    +

    The domain of the cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var domain: String?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + expires + +
    +
    +
    +
    +
    +
    +

    The cookie’s expiration date.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var expires: Date?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + maxAge + +
    +
    +
    +
    +
    +
    +

    The cookie’s age in seconds.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var maxAge: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + httpOnly + +
    +
    +
    +
    +
    +
    +

    Whether the cookie should only be sent to HTTP servers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var httpOnly: Bool
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + secure + +
    +
    +
    +
    +
    +
    +

    Whether the cookie should only be sent over secure channels.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var secure: Bool
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a Cookie by parsing a Set-Cookie header.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(header: String, defaultDomain: String)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + header + + +
    +

    String representation of the Set-Cookie response header.

    +
    +
    + + defaultDomain + + +
    +

    Default domain to use if cookie was sent without one.

    +
    +
    +
    +
    +

    Return Value

    +

    nil if the header is invalid.

    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(name: String, value: String, path: String = "/", domain: String? = nil, expires: Date? = nil, maxAge: Int? = nil, httpOnly: Bool = false, secure: Bool = false)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + name + + +
    +

    The name of the cookie.

    +
    +
    + + value + + +
    +

    The cookie’s string value.

    +
    +
    + + path + + +
    +

    The cookie’s path.

    +
    +
    + + domain + + +
    +

    The domain of the cookie, defaults to nil.

    +
    +
    + + expires + + +
    +

    The cookie’s expiration date, defaults to nil.

    +
    +
    + + maxAge + + +
    +

    The cookie’s age in seconds, defaults to nil.

    +
    +
    + + httpOnly + + +
    +

    Whether this cookie should be used by HTTP servers only, defaults to false.

    +
    +
    + + secure + + +
    +

    Whether this cookie should only be sent using secure channels, defaults to false.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Decompression.html b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Decompression.html new file mode 100644 index 000000000..6c5d5abf1 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Decompression.html @@ -0,0 +1,237 @@ + + + + Decompression Enumeration Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Decompression

+
+
+ +
public enum Decompression
+ +
+
+

Specifies decompression settings.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + disabled + +
    +
    +
    +
    +
    +
    +

    Decompression is disabled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case disabled
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + enabled(limit:) + +
    +
    +
    +
    +
    +
    +

    Decompression is enabled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case enabled(limit: NIOHTTPDecompression.DecompressionLimit)
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html new file mode 100644 index 000000000..8a5bbf4da --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html @@ -0,0 +1,237 @@ + + + + EventLoopGroupProvider Enumeration Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

EventLoopGroupProvider

+
+
+ +
public enum EventLoopGroupProvider
+ +
+
+

Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + shared(_:) + +
    +
    +
    +
    +
    +
    +

    EventLoopGroup will be provided by the user. Owner of this group is responsible for its lifecycle.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case shared(EventLoopGroup)
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + createNew + +
    +
    +
    +
    +
    +
    +

    EventLoopGroup will be created by the client. When syncShutdown is called, created EventLoopGroup will be shut down as well.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case createNew
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html new file mode 100644 index 000000000..11376e670 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html @@ -0,0 +1,304 @@ + + + + EventLoopPreference Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

EventLoopPreference

+
+
+ +
public struct EventLoopPreference
+
extension HTTPClient.EventLoopPreference: CustomStringConvertible
+ +
+
+

Specifies how the library will treat event loop passed by the user.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + indifferent + +
    +
    +
    +
    +
    +
    +

    Event Loop will be selected by the library.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let indifferent: HTTPClient.EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + delegate(on:) + +
    +
    +
    +
    +
    +
    +

    The delegate will be run on the specified EventLoop (and the Channel if possible).

    + +

    This will call the configured delegate on eventLoop and will try to use a Channel on the same +EventLoop but will not establish a new network connection just to satisfy the EventLoop preference if +another existing connection on a different EventLoop is readily available from a connection pool.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func delegate(on eventLoop: EventLoop) -> EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The delegate and the Channel will be run on the specified EventLoop.

    + +

    Use this for use-cases where you prefer a new connection to be established over re-using an existing +connection that might be on a different EventLoop.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func delegateAndChannel(on eventLoop: EventLoop) -> EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var description: String { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/NWPOSIXError.html b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/NWPOSIXError.html new file mode 100644 index 000000000..9f9b2d320 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/NWPOSIXError.html @@ -0,0 +1,222 @@ + + + + NWPOSIXError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

NWPOSIXError

+ +
+
+ +
+
+
+
    +
  • +
    + + + + errorCode + +
    +
    +
    +
    +
    +
    +

    POSIX error code (enum)

    + +
    +
    +
    +
  • +
  • +
    + + + + init(_:reason:) + +
    +
    +
    +
    +
    +
    +

    Initialise a NWPOSIXError

    + +
    +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/NWTLSError.html b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/NWTLSError.html new file mode 100644 index 000000000..b049b2f07 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/NWTLSError.html @@ -0,0 +1,222 @@ + + + + NWTLSError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

NWTLSError

+ +
+
+ +
+
+
+
    +
  • +
    + + + + status + +
    +
    +
    +
    +
    +
    +

    TLS error status. List of TLS errors can be found in

    + +
    +
    +
    +
  • +
  • +
    + + + + init(_:reason:) + +
    +
    +
    +
    +
    +
    +

    initialise a NWTLSError

    + +
    +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Request.html b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Request.html new file mode 100644 index 000000000..f9bd3a5ae --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Request.html @@ -0,0 +1,875 @@ + + + + Request Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Request

+
+
+ +
public struct Request
+ +
+
+

Represent HTTP request.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + method + +
    +
    +
    +
    +
    +
    +

    Request HTTP method, defaults to GET.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let method: HTTPMethod
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + url + +
    +
    +
    +
    +
    +
    +

    Remote URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let url: URL
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + scheme + +
    +
    +
    +
    +
    +
    +

    Remote HTTP scheme, resolved from URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var scheme: String { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + headers + +
    +
    +
    +
    +
    +
    +

    Request custom HTTP Headers, defaults to no headers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var headers: HTTPHeaders
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + body + +
    +
    +
    +
    +
    +
    +

    Request body, defaults to no body.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var body: Body?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + tlsConfiguration + +
    +
    +
    +
    +
    +
    +

    Request-specific TLS configuration, defaults to no request-specific TLS configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var tlsConfiguration: TLSConfiguration?
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP request.

    +
    +

    Throws

    +
      +
    • invalidURL if URL cannot be parsed.
    • +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: String, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + version + + +
    +

    HTTP version.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP request.

    +
    +

    Throws

    +
      +
    • invalidURL if URL cannot be parsed.
    • +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: String, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil, tlsConfiguration: TLSConfiguration?) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + version + + +
    +

    HTTP version.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + tlsConfiguration + + +
    +

    Request TLS configuration

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTP Request.

    +
    +

    Throws

    +
      +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    • missingSocketPath if URL does not contains a socketPath as an encoded host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTP Request.

    +
    +

    Throws

    +
      +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    • missingSocketPath if URL does not contains a socketPath as an encoded host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil, tlsConfiguration: TLSConfiguration?) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + tlsConfiguration + + +
    +

    Request TLS configuration

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Remote host, resolved from URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + port + +
    +
    +
    +
    +
    +
    +

    Resolved port.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var port: Int { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + useTLS + +
    +
    +
    +
    +
    +
    +

    Whether request will be executed using secure socket.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var useTLS: Bool { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Response.html b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Response.html new file mode 100644 index 000000000..fc2c1c146 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Response.html @@ -0,0 +1,540 @@ + + + + Response Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Response

+
+
+ +
public struct Response
+ +
+
+

Represent HTTP response.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Remote host of the request.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + status + +
    +
    +
    +
    +
    +
    +

    Response HTTP status.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var status: HTTPResponseStatus
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + version + +
    +
    +
    +
    +
    +
    +

    Response HTTP version.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var version: HTTPVersion
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + headers + +
    +
    +
    +
    +
    +
    +

    Reponse HTTP headers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var headers: HTTPHeaders
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + body + +
    +
    +
    +
    +
    +
    +

    Response body.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var body: ByteBuffer?
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP Response.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    @available(*, deprecated, renamed: "init(host:status:version:headers:body:﹚")
    +public init(host: String, status: HTTPResponseStatus, headers: HTTPHeaders, body: ByteBuffer?)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + host + + +
    +

    Remote host of the request.

    +
    +
    + + status + + +
    +

    Response HTTP status.

    +
    +
    + + headers + + +
    +

    Reponse HTTP headers.

    +
    +
    + + body + + +
    +

    Response body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP Response.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(host: String, status: HTTPResponseStatus, version: HTTPVersion, headers: HTTPHeaders, body: ByteBuffer?)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + host + + +
    +

    Remote host of the request.

    +
    +
    + + status + + +
    +

    Response HTTP status.

    +
    +
    + + version + + +
    +

    Response HTTP version.

    +
    +
    + + headers + + +
    +

    Reponse HTTP headers.

    +
    +
    + + body + + +
    +

    Response body.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + cookies + +
    +
    +
    +
    +
    +
    +

    List of HTTP cookies returned by the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var cookies: [HTTPClient.Cookie] { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Task.html b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Task.html new file mode 100644 index 000000000..5dd34fc5d --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClient/Task.html @@ -0,0 +1,307 @@ + + + + Task Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Task

+
+
+ +
public final class Task<Response>
+ +
+
+

Response execution context. Will be created by the library and could be used for obtaining +EventLoopFuture<Response> of the execution or cancellation of the execution.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + eventLoop + +
    +
    +
    +
    +
    +
    +

    The EventLoop the delegate will be executed on.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let eventLoop: EventLoop
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + futureResult + +
    +
    +
    +
    +
    +
    +

    EventLoopFuture for the response returned by this request.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var futureResult: EventLoopFuture<Response> { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + wait() + +
    +
    +
    +
    +
    +
    +

    Waits for execution of this request to complete.

    +
    +

    Throws

    + The error value of the EventLoopFuture if it errors. + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func wait() throws -> Response
    + +
    +
    +
    +

    Return Value

    +

    The value of the EventLoopFuture when it completes.

    +
    + +
    +
    +
  • +
  • +
    + + + + cancel() + +
    +
    +
    +
    +
    +
    +

    Cancels the request execution.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func cancel()
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html new file mode 100644 index 000000000..35c075399 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html @@ -0,0 +1,295 @@ + + + + HTTPClientCopyingDelegate Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientCopyingDelegate

+
+
+ +
public final class HTTPClientCopyingDelegate : HTTPClientResponseDelegate
+ +
+
+

Undocumented

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Classes/ResponseAccumulator.html b/docs/1.8.1/AsyncHTTPClient/Classes/ResponseAccumulator.html new file mode 100644 index 000000000..bab577b4c --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Classes/ResponseAccumulator.html @@ -0,0 +1,353 @@ + + + + ResponseAccumulator Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

ResponseAccumulator

+
+
+ +
public class ResponseAccumulator : HTTPClientResponseDelegate
+ +
+
+

Undocumented

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Extensions.html b/docs/1.8.1/AsyncHTTPClient/Extensions.html new file mode 100644 index 000000000..e9d284c18 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Extensions.html @@ -0,0 +1,194 @@ + + + + Extensions Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Extensions

+

The following extensions are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + URL + +
    +
    +
    +
    +
    +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    extension URL
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Extensions/URL.html b/docs/1.8.1/AsyncHTTPClient/Extensions/URL.html new file mode 100644 index 000000000..236bddfd9 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Extensions/URL.html @@ -0,0 +1,295 @@ + + + + URL Extension Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

URL

+
+
+ +
extension URL
+ +
+
+ +
+
+ +
+
+
+
    +
  • + +
    +
    +
    +
    +
    +

    Initializes a newly created HTTP URL connecting to a unix domain socket path. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “http+unix” scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(httpURLWithSocketPath socketPath: String, uri: String = "/")
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + uri + + +
    +

    The URI path and query that will be sent to the server.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Initializes a newly created HTTPS URL connecting to a unix domain socket path over TLS. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “https+unix” scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(httpsURLWithSocketPath socketPath: String, uri: String = "/")
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + uri + + +
    +

    The URI path and query that will be sent to the server.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Protocols.html b/docs/1.8.1/AsyncHTTPClient/Protocols.html new file mode 100644 index 000000000..324ac20c9 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Protocols.html @@ -0,0 +1,230 @@ + + + + Protocols Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Protocols

+

The following protocols are available globally.

+ +
+
+ +
+
+
+
    +
  • + +
    +
    +
    +
    +
    +

    HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed. +You can implement this protocol if you need fine-grained control over an HTTP request/response, for example, if you want to inspect the response +headers before deciding whether to accept a response body, or if you want to stream your request body. Pass an instance of your conforming +class to the HTTPClient.execute() method and this package will call each delegate method appropriately as the request takes place./

    +

    Backpressure

    + +

    A HTTPClientResponseDelegate can be used to exert backpressure on the server response. This is achieved by way of the futures returned from +didReceiveHead and didReceiveBodyPart. The following functions are part of the “backpressure system” in the delegate:

    + +
      +
    • didReceiveHead
    • +
    • didReceiveBodyPart
    • +
    • didFinishRequest
    • +
    • didReceiveError
    • +
    + +

    The first three methods are strictly exclusive, with that exclusivity managed by the futures returned by didReceiveHead and +didReceiveBodyPart. What this means is that until the returned future is completed, none of these three methods will be called +again. This allows delegates to rate limit the server to a capacity it can manage. didFinishRequest does not return a future, +as we are expecting no more data from the server at this time.

    + +

    didReceiveError is somewhat special: it signals the end of this regime. didRecieveError is not exclusive: it may be called at +any time, even if a returned future is not yet completed. didReceiveError is terminal, meaning that once it has been called none +of these four methods will be called again. This can be used as a signal to abandon all outstanding work.

    +
    +

    Note

    + This delegate is strongly held by the HTTPTaskHandler + for the duration of the Request processing and will be + released together with the HTTPTaskHandler when channel is closed. + Users of the library are not required to keep a reference to the + object that implements this protocol, but may do so if needed. + +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public protocol HTTPClientResponseDelegate : AnyObject
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html b/docs/1.8.1/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html new file mode 100644 index 000000000..659162c54 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html @@ -0,0 +1,712 @@ + + + + HTTPClientResponseDelegate Protocol Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientResponseDelegate

+
+
+ +
public protocol HTTPClientResponseDelegate : AnyObject
+ +
+
+

HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed. +You can implement this protocol if you need fine-grained control over an HTTP request/response, for example, if you want to inspect the response +headers before deciding whether to accept a response body, or if you want to stream your request body. Pass an instance of your conforming +class to the HTTPClient.execute() method and this package will call each delegate method appropriately as the request takes place./

+

Backpressure

+ +

A HTTPClientResponseDelegate can be used to exert backpressure on the server response. This is achieved by way of the futures returned from +didReceiveHead and didReceiveBodyPart. The following functions are part of the “backpressure system” in the delegate:

+ +
    +
  • didReceiveHead
  • +
  • didReceiveBodyPart
  • +
  • didFinishRequest
  • +
  • didReceiveError
  • +
+ +

The first three methods are strictly exclusive, with that exclusivity managed by the futures returned by didReceiveHead and +didReceiveBodyPart. What this means is that until the returned future is completed, none of these three methods will be called +again. This allows delegates to rate limit the server to a capacity it can manage. didFinishRequest does not return a future, +as we are expecting no more data from the server at this time.

+ +

didReceiveError is somewhat special: it signals the end of this regime. didRecieveError is not exclusive: it may be called at +any time, even if a returned future is not yet completed. didReceiveError is terminal, meaning that once it has been called none +of these four methods will be called again. This can be used as a signal to abandon all outstanding work.

+
+

Note

+ This delegate is strongly held by the HTTPTaskHandler + for the duration of the Request processing and will be + released together with the HTTPTaskHandler when channel is closed. + Users of the library are not required to keep a reference to the + object that implements this protocol, but may do so if needed. + +
+ + +
+
+ +
+
+
+
    +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    associatedtype Response
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequestHead(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when the request head is sent. Will be called once.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequestHead(task: HTTPClient.Task<Response>, _ head: HTTPRequestHead)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + head + + +
    +

    Request head.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequestPart(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when a part of the request body is sent. Could be called zero or more times.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequestPart(task: HTTPClient.Task<Response>, _ part: IOData)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + part + + +
    +

    Request body Part.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequest(task:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when the request is fully sent. Will be called once.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequest(task: HTTPClient.Task<Response>)
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveHead(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when response head is received. Will be called once. +You must return an EventLoopFuture<Void> that you complete when you have finished processing the body part. +You can create an already succeeded future by calling task.eventLoop.makeSucceededFuture(()).

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveHead(task: HTTPClient.Task<Response>, _ head: HTTPResponseHead) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + head + + +
    +

    Received reposonse head.

    +
    +
    +
    +
    +

    Return Value

    +

    EventLoopFuture that will be used for backpressure.

    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveBodyPart(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when part of a response body is received. Could be called zero or more times. +You must return an EventLoopFuture<Void> that you complete when you have finished processing the body part. +You can create an already succeeded future by calling task.eventLoop.makeSucceededFuture(()).

    + +

    This function will not be called until the future returned by didReceiveHead has completed.

    + +

    This function will not be called for subsequent body parts until the previous future returned by a +call to this function completes.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveBodyPart(task: HTTPClient.Task<Response>, _ buffer: ByteBuffer) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + buffer + + +
    +

    Received body Part.

    +
    +
    +
    +
    +

    Return Value

    +

    EventLoopFuture that will be used for backpressure.

    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveError(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when error was thrown during request execution. Will be called zero or one time only. Request processing will be stopped after that.

    + +

    This function may be called at any time: it does not respect the backpressure exerted by didReceiveHead and didReceiveBodyPart. +All outstanding work may be cancelled when this is received. Once called, no further calls will be made to didReceiveHead, didReceiveBodyPart, +or didFinishRequest.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveError(task: HTTPClient.Task<Response>, _ error: Error)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + error + + +
    +

    Error that occured during response processing.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Called when the complete HTTP request is finished. You must return an instance of your Response associated type. Will be called once, except if an error occurred.

    + +

    This function will not be called until all futures returned by didReceiveHead and didReceiveBodyPart have completed. Once called, +no further calls will be made to didReceiveHead, didReceiveBodyPart, or didReceiveError.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didFinishRequest(task: HTTPClient.Task<Response>) throws -> Response
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    +
    +
    +

    Return Value

    +

    Result of processing.

    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Structs.html b/docs/1.8.1/AsyncHTTPClient/Structs.html new file mode 100644 index 000000000..9b606ce7a --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Structs.html @@ -0,0 +1,198 @@ + + + + Structures Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Structures

+

The following structures are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + HTTPClientError + +
    +
    +
    +
    +
    +
    +

    Possible client errors.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct HTTPClientError : Error, Equatable, CustomStringConvertible
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/Structs/HTTPClientError.html b/docs/1.8.1/AsyncHTTPClient/Structs/HTTPClientError.html new file mode 100644 index 000000000..98824328b --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/Structs/HTTPClientError.html @@ -0,0 +1,1146 @@ + + + + HTTPClientError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientError

+
+
+ +
public struct HTTPClientError : Error, Equatable, CustomStringConvertible
+ +
+
+

Possible client errors.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var description: String { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + invalidURL + +
    +
    +
    +
    +
    +
    +

    URL provided is invalid.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let invalidURL: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + emptyHost + +
    +
    +
    +
    +
    +
    +

    URL does not contain host.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let emptyHost: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + missingSocketPath + +
    +
    +
    +
    +
    +
    +

    URL does not contain a socketPath as a host for http(s)+unix shemes.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let missingSocketPath: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + alreadyShutdown + +
    +
    +
    +
    +
    +
    +

    Client is shutdown and cannot be used for new requests.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let alreadyShutdown: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + emptyScheme + +
    +
    +
    +
    +
    +
    +

    URL does not contain scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let emptyScheme: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + unsupportedScheme(_:) + +
    +
    +
    +
    +
    +
    +

    Provided URL scheme is not supported, supported schemes are: http and https

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func unsupportedScheme(_ scheme: String) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + readTimeout + +
    +
    +
    +
    +
    +
    +

    Request timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let readTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Remote connection was closed unexpectedly.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let remoteConnectionClosed: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + cancelled + +
    +
    +
    +
    +
    +
    +

    Request was cancelled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let cancelled: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Request contains invalid identity encoding.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let identityCodingIncorrectlyPresent: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Request contains multiple chunks definitions.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    @available(*, deprecated, message: "AsyncHTTPClient now silently corrects this invalid header.")
    +public static let chunkedSpecifiedMultipleTimes: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + invalidProxyResponse + +
    +
    +
    +
    +
    +
    +

    Proxy response was invalid.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let invalidProxyResponse: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + contentLengthMissing + +
    +
    +
    +
    +
    +
    +

    Request does not contain Content-Length header.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let contentLengthMissing: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Proxy Authentication Required.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let proxyAuthenticationRequired: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + redirectLimitReached + +
    +
    +
    +
    +
    +
    +

    Redirect Limit reached.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let redirectLimitReached: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + redirectCycleDetected + +
    +
    +
    +
    +
    +
    +

    Redirect Cycle detected.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let redirectCycleDetected: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + uncleanShutdown + +
    +
    +
    +
    +
    +
    +

    Unclean shutdown.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let uncleanShutdown: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + traceRequestWithBody + +
    +
    +
    +
    +
    +
    +

    A body was sent in a request with method TRACE.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let traceRequestWithBody: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Header field names contain invalid characters.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func invalidHeaderFieldNames(_ names: [String]) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + bodyLengthMismatch + +
    +
    +
    +
    +
    +
    +

    Body length is not equal to Content-Length.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let bodyLengthMismatch: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + writeAfterRequestSent + +
    +
    +
    +
    +
    +
    +

    Body part was written after request was fully sent.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let writeAfterRequestSent: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + incompatibleHeaders + +
    +
    +
    +
    +
    +
    +

    Incompatible headers specified, for example Transfer-Encoding and Content-Length.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    @available(*, deprecated, message: "AsyncHTTPClient now silently corrects invalid headers.")
    +public static let incompatibleHeaders: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + connectTimeout + +
    +
    +
    +
    +
    +
    +

    Creating a new tcp connection timed out

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let connectTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + socksHandshakeTimeout + +
    +
    +
    +
    +
    +
    +

    The socks handshake timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let socksHandshakeTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The http proxy connection creation timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let httpProxyHandshakeTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + tlsHandshakeTimeout + +
    +
    +
    +
    +
    +
    +

    The tls handshake timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let tlsHandshakeTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The remote server only offered an unsupported application protocol

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func serverOfferedUnsupportedApplicationProtocol(_ proto: String) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + deadlineExceeded + +
    +
    +
    +
    +
    +
    +

    The request deadline was exceeded. The request was cancelled because of this.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let deadlineExceeded: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The remote server responded with a status code >= 300, before the full request was sent. The request stream +was therefore cancelled

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let requestStreamCancelled: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Aquiring a HTTP connection from the connection pool timed out.

    + +

    This can have multiple reasons:

    + +
      +
    • A connection could not be created within the timout period.
    • +
    • Tasks are not processed fast enough on the existing connections, to process all waiters in time
    • +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let getConnectionFromPoolTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let httpEndReceivedAfterHeadWith1xx: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/badge.svg b/docs/1.8.1/AsyncHTTPClient/badge.svg new file mode 100644 index 000000000..be498fa5c --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/badge.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + documentation + + + documentation + + + 86% + + + 86% + + + diff --git a/docs/1.8.1/AsyncHTTPClient/css/highlight.css b/docs/1.8.1/AsyncHTTPClient/css/highlight.css new file mode 100644 index 000000000..c170357ce --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/css/highlight.css @@ -0,0 +1,202 @@ +/*! Jazzy - https://github.com/realm/jazzy + * Copyright Realm Inc. + * SPDX-License-Identifier: MIT + */ +/* Credit to https://gist.github.com/wataru420/2048287 */ +.highlight .c { + color: #999988; + font-style: italic; } + +.highlight .err { + color: #a61717; + background-color: #e3d2d2; } + +.highlight .k { + color: #000000; + font-weight: bold; } + +.highlight .o { + color: #000000; + font-weight: bold; } + +.highlight .cm { + color: #999988; + font-style: italic; } + +.highlight .cp { + color: #999999; + font-weight: bold; } + +.highlight .c1 { + color: #999988; + font-style: italic; } + +.highlight .cs { + color: #999999; + font-weight: bold; + font-style: italic; } + +.highlight .gd { + color: #000000; + background-color: #ffdddd; } + +.highlight .gd .x { + color: #000000; + background-color: #ffaaaa; } + +.highlight .ge { + color: #000000; + font-style: italic; } + +.highlight .gr { + color: #aa0000; } + +.highlight .gh { + color: #999999; } + +.highlight .gi { + color: #000000; + background-color: #ddffdd; } + +.highlight .gi .x { + color: #000000; + background-color: #aaffaa; } + +.highlight .go { + color: #888888; } + +.highlight .gp { + color: #555555; } + +.highlight .gs { + font-weight: bold; } + +.highlight .gu { + color: #aaaaaa; } + +.highlight .gt { + color: #aa0000; } + +.highlight .kc { + color: #000000; + font-weight: bold; } + +.highlight .kd { + color: #000000; + font-weight: bold; } + +.highlight .kp { + color: #000000; + font-weight: bold; } + +.highlight .kr { + color: #000000; + font-weight: bold; } + +.highlight .kt { + color: #445588; } + +.highlight .m { + color: #009999; } + +.highlight .s { + color: #d14; } + +.highlight .na { + color: #008080; } + +.highlight .nb { + color: #0086B3; } + +.highlight .nc { + color: #445588; + font-weight: bold; } + +.highlight .no { + color: #008080; } + +.highlight .ni { + color: #800080; } + +.highlight .ne { + color: #990000; + font-weight: bold; } + +.highlight .nf { + color: #990000; } + +.highlight .nn { + color: #555555; } + +.highlight .nt { + color: #000080; } + +.highlight .nv { + color: #008080; } + +.highlight .ow { + color: #000000; + font-weight: bold; } + +.highlight .w { + color: #bbbbbb; } + +.highlight .mf { + color: #009999; } + +.highlight .mh { + color: #009999; } + +.highlight .mi { + color: #009999; } + +.highlight .mo { + color: #009999; } + +.highlight .sb { + color: #d14; } + +.highlight .sc { + color: #d14; } + +.highlight .sd { + color: #d14; } + +.highlight .s2 { + color: #d14; } + +.highlight .se { + color: #d14; } + +.highlight .sh { + color: #d14; } + +.highlight .si { + color: #d14; } + +.highlight .sx { + color: #d14; } + +.highlight .sr { + color: #009926; } + +.highlight .s1 { + color: #d14; } + +.highlight .ss { + color: #990073; } + +.highlight .bp { + color: #999999; } + +.highlight .vc { + color: #008080; } + +.highlight .vg { + color: #008080; } + +.highlight .vi { + color: #008080; } + +.highlight .il { + color: #009999; } diff --git a/docs/1.8.1/AsyncHTTPClient/css/jazzy.css b/docs/1.8.1/AsyncHTTPClient/css/jazzy.css new file mode 100644 index 000000000..c7bb9fe22 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/css/jazzy.css @@ -0,0 +1,404 @@ +/*! Jazzy - https://github.com/realm/jazzy + * Copyright Realm Inc. + * SPDX-License-Identifier: MIT + */ +*, *:before, *:after { + box-sizing: inherit; } + +body { + margin: 0; + background: #fff; + color: #333; + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + letter-spacing: .2px; + -webkit-font-smoothing: antialiased; + box-sizing: border-box; } + +h1 { + font-size: 2rem; + font-weight: 700; + margin: 1.275em 0 0.6em; } + +h2 { + font-size: 1.75rem; + font-weight: 700; + margin: 1.275em 0 0.3em; } + +h3 { + font-size: 1.5rem; + font-weight: 700; + margin: 1em 0 0.3em; } + +h4 { + font-size: 1.25rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h5 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h6 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; + color: #777; } + +p { + margin: 0 0 1em; } + +ul, ol { + padding: 0 0 0 2em; + margin: 0 0 0.85em; } + +blockquote { + margin: 0 0 0.85em; + padding: 0 15px; + color: #858585; + border-left: 4px solid #e5e5e5; } + +img { + max-width: 100%; } + +a { + color: #4183c4; + text-decoration: none; } + a:hover, a:focus { + outline: 0; + text-decoration: underline; } + a.discouraged { + text-decoration: line-through; } + a.discouraged:hover, a.discouraged:focus { + text-decoration: underline line-through; } + +table { + background: #fff; + width: 100%; + border-collapse: collapse; + border-spacing: 0; + overflow: auto; + margin: 0 0 0.85em; } + +tr:nth-child(2n) { + background-color: #fbfbfb; } + +th, td { + padding: 6px 13px; + border: 1px solid #ddd; } + +hr { + height: 1px; + border: none; + background-color: #ddd; } + +pre { + margin: 0 0 1.275em; + padding: .85em 1em; + overflow: auto; + background: #f7f7f7; + font-size: .85em; + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +code { + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +.item-container p > code, .item-container li > code, .top-matter p > code, .top-matter li > code { + background: #f7f7f7; + padding: .2em; } + .item-container p > code:before, .item-container p > code:after, .item-container li > code:before, .item-container li > code:after, .top-matter p > code:before, .top-matter p > code:after, .top-matter li > code:before, .top-matter li > code:after { + letter-spacing: -.2em; + content: "\00a0"; } + +pre code { + padding: 0; + white-space: pre; } + +.content-wrapper { + display: flex; + flex-direction: column; } + @media (min-width: 768px) { + .content-wrapper { + flex-direction: row; } } +.header { + display: flex; + padding: 8px; + font-size: 0.875em; + background: #444; + color: #999; } + +.header-col { + margin: 0; + padding: 0 8px; } + +.header-col--primary { + flex: 1; } + +.header-link { + color: #fff; } + +.header-icon { + padding-right: 2px; + vertical-align: -3px; + height: 16px; } + +.breadcrumbs { + font-size: 0.875em; + padding: 8px 16px; + margin: 0; + background: #fbfbfb; + border-bottom: 1px solid #ddd; } + +.carat { + height: 10px; + margin: 0 5px; } + +.navigation { + order: 2; } + @media (min-width: 768px) { + .navigation { + order: 1; + width: 25%; + max-width: 300px; + padding-bottom: 64px; + overflow: hidden; + word-wrap: normal; + background: #fbfbfb; + border-right: 1px solid #ddd; } } +.nav-groups { + list-style-type: none; + padding-left: 0; } + +.nav-group-name { + border-bottom: 1px solid #ddd; + padding: 8px 0 8px 16px; } + +.nav-group-name-link { + color: #333; } + +.nav-group-tasks { + margin: 8px 0; + padding: 0 0 0 8px; } + +.nav-group-task { + font-size: 1em; + list-style-type: none; + white-space: nowrap; } + +.nav-group-task-link { + color: #808080; } + +.main-content { + order: 1; } + @media (min-width: 768px) { + .main-content { + order: 2; + flex: 1; + padding-bottom: 60px; } } +.section { + padding: 0 32px; + border-bottom: 1px solid #ddd; } + +.section-content { + max-width: 834px; + margin: 0 auto; + padding: 16px 0; } + +.section-name { + color: #666; + display: block; } + .section-name p { + margin-bottom: inherit; } + +.declaration .highlight { + overflow-x: initial; + padding: 8px 0; + margin: 0; + background-color: transparent; + border: none; } + +.task-group-section { + border-top: 1px solid #ddd; } + +.task-group { + padding-top: 0px; } + +.task-name-container a[name]:before { + content: ""; + display: block; } + +.section-name-container { + position: relative; } + .section-name-container .section-name-link { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + margin-bottom: 0; } + .section-name-container .section-name { + position: relative; + pointer-events: none; + z-index: 1; } + .section-name-container .section-name a { + pointer-events: auto; } + +.item-container { + padding: 0; } + +.item { + padding-top: 8px; + width: 100%; + list-style-type: none; } + .item a[name]:before { + content: ""; + display: block; } + .item .token, .item .direct-link { + display: inline-block; + text-indent: -20px; + padding-left: 3px; + margin-left: 20px; + font-size: 1rem; } + .item .declaration-note { + font-size: .85em; + color: #808080; + font-style: italic; } + +.pointer-container { + border-bottom: 1px solid #ddd; + left: -23px; + padding-bottom: 13px; + position: relative; + width: 110%; } + +.pointer { + left: 21px; + top: 7px; + display: block; + position: absolute; + width: 12px; + height: 12px; + border-left: 1px solid #ddd; + border-top: 1px solid #ddd; + background: #fff; + transform: rotate(45deg); } + +.height-container { + display: none; + position: relative; + width: 100%; + overflow: hidden; } + .height-container .section { + background: #fff; + border: 1px solid #ddd; + border-top-width: 0; + padding-top: 10px; + padding-bottom: 5px; + padding: 8px 16px; } + +.aside, .language { + padding: 6px 12px; + margin: 12px 0; + border-left: 5px solid #dddddd; + overflow-y: hidden; } + .aside .aside-title, .language .aside-title { + font-size: 9px; + letter-spacing: 2px; + text-transform: uppercase; + padding-bottom: 0; + margin: 0; + color: #aaa; + -webkit-user-select: none; } + .aside p:last-child, .language p:last-child { + margin-bottom: 0; } + +.language { + border-left: 5px solid #cde9f4; } + .language .aside-title { + color: #4183c4; } + +.aside-warning, .aside-deprecated, .aside-unavailable { + border-left: 5px solid #ff6666; } + .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { + color: #ff0000; } + +.graybox { + border-collapse: collapse; + width: 100%; } + .graybox p { + margin: 0; + word-break: break-word; + min-width: 50px; } + .graybox td { + border: 1px solid #ddd; + padding: 5px 25px 5px 10px; + vertical-align: middle; } + .graybox tr td:first-of-type { + text-align: right; + padding: 7px; + vertical-align: top; + word-break: normal; + width: 40px; } + +.slightly-smaller { + font-size: 0.9em; } + +.footer { + padding: 8px 16px; + background: #444; + color: #ddd; + font-size: 0.8em; } + .footer p { + margin: 8px 0; } + .footer a { + color: #fff; } + +html.dash .header, html.dash .breadcrumbs, html.dash .navigation { + display: none; } + +html.dash .height-container { + display: block; } + +form[role=search] input { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 24px; + padding: 0 10px; + margin: 0; + border: none; + border-radius: 1em; } + .loading form[role=search] input { + background: white url(http://github.com/img/spinner.gif) center right 4px no-repeat; } + +form[role=search] .tt-menu { + margin: 0; + min-width: 300px; + background: #fbfbfb; + color: #333; + border: 1px solid #ddd; } + +form[role=search] .tt-highlight { + font-weight: bold; } + +form[role=search] .tt-suggestion { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 0 8px; } + form[role=search] .tt-suggestion span { + display: table-cell; + white-space: nowrap; } + form[role=search] .tt-suggestion .doc-parent-name { + width: 100%; + text-align: right; + font-weight: normal; + font-size: 0.9em; + padding-left: 16px; } + +form[role=search] .tt-suggestion:hover, +form[role=search] .tt-suggestion.tt-cursor { + cursor: pointer; + background-color: #4183c4; + color: #fff; } + +form[role=search] .tt-suggestion:hover .doc-parent-name, +form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { + color: #fff; } diff --git a/docs/1.8.1/AsyncHTTPClient/img/carat.png b/docs/1.8.1/AsyncHTTPClient/img/carat.png new file mode 100755 index 000000000..29d2f7fd4 Binary files /dev/null and b/docs/1.8.1/AsyncHTTPClient/img/carat.png differ diff --git a/docs/1.8.1/AsyncHTTPClient/img/dash.png b/docs/1.8.1/AsyncHTTPClient/img/dash.png new file mode 100755 index 000000000..6f694c7a0 Binary files /dev/null and b/docs/1.8.1/AsyncHTTPClient/img/dash.png differ diff --git a/docs/1.8.1/AsyncHTTPClient/img/gh.png b/docs/1.8.1/AsyncHTTPClient/img/gh.png new file mode 100755 index 000000000..628da97c7 Binary files /dev/null and b/docs/1.8.1/AsyncHTTPClient/img/gh.png differ diff --git a/docs/1.8.1/AsyncHTTPClient/img/spinner.gif b/docs/1.8.1/AsyncHTTPClient/img/spinner.gif new file mode 100644 index 000000000..e3038d0a4 Binary files /dev/null and b/docs/1.8.1/AsyncHTTPClient/img/spinner.gif differ diff --git a/docs/1.8.1/AsyncHTTPClient/index.html b/docs/1.8.1/AsyncHTTPClient/index.html new file mode 100644 index 000000000..c48258fc0 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/index.html @@ -0,0 +1,164 @@ + + + + AsyncHTTPClient Reference + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.8.1 Docs + + (86% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+ +

AsyncHTTPClient Docs

+ +

AsyncHTTPClient is a Swift HTTP Client package.

+ +

To get started with AsyncHTTPClient, import AsyncHTTPClient. The +most important type is HTTPClient +which you can use to emit log messages.

+ +
+
+ + +
+
+ + + diff --git a/docs/1.8.1/AsyncHTTPClient/js/jazzy.js b/docs/1.8.1/AsyncHTTPClient/js/jazzy.js new file mode 100755 index 000000000..198441660 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/js/jazzy.js @@ -0,0 +1,74 @@ +// Jazzy - https://github.com/realm/jazzy +// Copyright Realm Inc. +// SPDX-License-Identifier: MIT + +window.jazzy = {'docset': false} +if (typeof window.dash != 'undefined') { + document.documentElement.className += ' dash' + window.jazzy.docset = true +} +if (navigator.userAgent.match(/xcode/i)) { + document.documentElement.className += ' xcode' + window.jazzy.docset = true +} + +function toggleItem($link, $content) { + var animationDuration = 300; + $link.toggleClass('token-open'); + $content.slideToggle(animationDuration); +} + +function itemLinkToContent($link) { + return $link.parent().parent().next(); +} + +// On doc load + hash-change, open any targetted item +function openCurrentItemIfClosed() { + if (window.jazzy.docset) { + return; + } + var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); + $content = itemLinkToContent($link); + if ($content.is(':hidden')) { + toggleItem($link, $content); + } +} + +$(openCurrentItemIfClosed); +$(window).on('hashchange', openCurrentItemIfClosed); + +// On item link ('token') click, toggle its discussion +$('.token').on('click', function(event) { + if (window.jazzy.docset) { + return; + } + var $link = $(this); + toggleItem($link, itemLinkToContent($link)); + + // Keeps the document from jumping to the hash. + var href = $link.attr('href'); + if (history.pushState) { + history.pushState({}, '', href); + } else { + location.hash = href; + } + event.preventDefault(); +}); + +// Clicks on links to the current, closed, item need to open the item +$("a:not('.token')").on('click', function() { + if (location == this.href) { + openCurrentItemIfClosed(); + } +}); + +// KaTeX rendering +if ("katex" in window) { + $($('.math').each( (_, element) => { + katex.render(element.textContent, element, { + displayMode: $(element).hasClass('m-block'), + throwOnError: false, + trust: true + }); + })) +} diff --git a/docs/1.8.1/AsyncHTTPClient/js/jazzy.search.js b/docs/1.8.1/AsyncHTTPClient/js/jazzy.search.js new file mode 100644 index 000000000..359cdbb8b --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/js/jazzy.search.js @@ -0,0 +1,74 @@ +// Jazzy - https://github.com/realm/jazzy +// Copyright Realm Inc. +// SPDX-License-Identifier: MIT + +$(function(){ + var $typeahead = $('[data-typeahead]'); + var $form = $typeahead.parents('form'); + var searchURL = $form.attr('action'); + + function displayTemplate(result) { + return result.name; + } + + function suggestionTemplate(result) { + var t = '
'; + t += '' + result.name + ''; + if (result.parent_name) { + t += '' + result.parent_name + ''; + } + t += '
'; + return t; + } + + $typeahead.one('focus', function() { + $form.addClass('loading'); + + $.getJSON(searchURL).then(function(searchData) { + const searchIndex = lunr(function() { + this.ref('url'); + this.field('name'); + this.field('abstract'); + for (const [url, doc] of Object.entries(searchData)) { + this.add({url: url, name: doc.name, abstract: doc.abstract}); + } + }); + + $typeahead.typeahead( + { + highlight: true, + minLength: 3, + autoselect: true + }, + { + limit: 10, + display: displayTemplate, + templates: { suggestion: suggestionTemplate }, + source: function(query, sync) { + const lcSearch = query.toLowerCase(); + const results = searchIndex.query(function(q) { + q.term(lcSearch, { boost: 100 }); + q.term(lcSearch, { + boost: 10, + wildcard: lunr.Query.wildcard.TRAILING + }); + }).map(function(result) { + var doc = searchData[result.ref]; + doc.url = result.ref; + return doc; + }); + sync(results); + } + } + ); + $form.removeClass('loading'); + $typeahead.trigger('focus'); + }); + }); + + var baseURL = searchURL.slice(0, -"search.json".length); + + $typeahead.on('typeahead:select', function(e, result) { + window.location = baseURL + result.url; + }); +}); diff --git a/docs/1.8.1/AsyncHTTPClient/js/jquery.min.js b/docs/1.8.1/AsyncHTTPClient/js/jquery.min.js new file mode 100644 index 000000000..c4c6022f2 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 00){var c=e.utils.clone(r)||{};c.position=[a,l],c.index=s.length,s.push(new e.Token(i.slice(a,o),c))}a=o+1}}return s},e.tokenizer.separator=/[\s\-]+/,e.Pipeline=function(){this._stack=[]},e.Pipeline.registeredFunctions=Object.create(null),e.Pipeline.registerFunction=function(t,r){r in this.registeredFunctions&&e.utils.warn("Overwriting existing registered function: "+r),t.label=r,e.Pipeline.registeredFunctions[t.label]=t},e.Pipeline.warnIfFunctionNotRegistered=function(t){var r=t.label&&t.label in this.registeredFunctions;r||e.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",t)},e.Pipeline.load=function(t){var r=new e.Pipeline;return t.forEach(function(t){var i=e.Pipeline.registeredFunctions[t];if(!i)throw new Error("Cannot load unregistered function: "+t);r.add(i)}),r},e.Pipeline.prototype.add=function(){var t=Array.prototype.slice.call(arguments);t.forEach(function(t){e.Pipeline.warnIfFunctionNotRegistered(t),this._stack.push(t)},this)},e.Pipeline.prototype.after=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,r)},e.Pipeline.prototype.before=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");this._stack.splice(i,0,r)},e.Pipeline.prototype.remove=function(e){var t=this._stack.indexOf(e);t!=-1&&this._stack.splice(t,1)},e.Pipeline.prototype.run=function(e){for(var t=this._stack.length,r=0;r1&&(se&&(r=n),s!=e);)i=r-t,n=t+Math.floor(i/2),s=this.elements[2*n];return s==e?2*n:s>e?2*n:sa?l+=2:o==a&&(t+=r[u+1]*i[l+1],u+=2,l+=2);return t},e.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},e.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),t=1,r=0;t0){var o,a=s.str.charAt(0);a in s.node.edges?o=s.node.edges[a]:(o=new e.TokenSet,s.node.edges[a]=o),1==s.str.length&&(o["final"]=!0),n.push({node:o,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(0!=s.editsRemaining){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new e.TokenSet;s.node.edges["*"]=u}if(0==s.str.length&&(u["final"]=!0),n.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&n.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),1==s.str.length&&(s.node["final"]=!0),s.str.length>=1){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new e.TokenSet;s.node.edges["*"]=l}1==s.str.length&&(l["final"]=!0),n.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var c,h=s.str.charAt(0),d=s.str.charAt(1);d in s.node.edges?c=s.node.edges[d]:(c=new e.TokenSet,s.node.edges[d]=c),1==s.str.length&&(c["final"]=!0),n.push({node:c,editsRemaining:s.editsRemaining-1,str:h+s.str.slice(2)})}}}return i},e.TokenSet.fromString=function(t){for(var r=new e.TokenSet,i=r,n=0,s=t.length;n=e;t--){var r=this.uncheckedNodes[t],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r["char"]]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}},e.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},e.Index.prototype.search=function(t){return this.query(function(r){var i=new e.QueryParser(t,r);i.parse()})},e.Index.prototype.query=function(t){for(var r=new e.Query(this.fields),i=Object.create(null),n=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),u=0;u1?this._b=1:this._b=e},e.Builder.prototype.k1=function(e){this._k1=e},e.Builder.prototype.add=function(t,r){var i=t[this._ref],n=Object.keys(this._fields);this._documents[i]=r||{},this.documentCount+=1;for(var s=0;s=this.length)return e.QueryLexer.EOS;var t=this.str.charAt(this.pos);return this.pos+=1,t},e.QueryLexer.prototype.width=function(){return this.pos-this.start},e.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},e.QueryLexer.prototype.backup=function(){this.pos-=1},e.QueryLexer.prototype.acceptDigitRun=function(){var t,r;do t=this.next(),r=t.charCodeAt(0);while(r>47&&r<58);t!=e.QueryLexer.EOS&&this.backup()},e.QueryLexer.prototype.more=function(){return this.pos1&&(t.backup(),t.emit(e.QueryLexer.TERM)),t.ignore(),t.more())return e.QueryLexer.lexText},e.QueryLexer.lexEditDistance=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.EDIT_DISTANCE),e.QueryLexer.lexText},e.QueryLexer.lexBoost=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.BOOST),e.QueryLexer.lexText},e.QueryLexer.lexEOS=function(t){t.width()>0&&t.emit(e.QueryLexer.TERM)},e.QueryLexer.termSeparator=e.tokenizer.separator,e.QueryLexer.lexText=function(t){for(;;){var r=t.next();if(r==e.QueryLexer.EOS)return e.QueryLexer.lexEOS;if(92!=r.charCodeAt(0)){if(":"==r)return e.QueryLexer.lexField;if("~"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexEditDistance;if("^"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexBoost;if("+"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if("-"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if(r.match(e.QueryLexer.termSeparator))return e.QueryLexer.lexTerm}else t.escapeCharacter()}},e.QueryParser=function(t,r){this.lexer=new e.QueryLexer(t),this.query=r,this.currentClause={},this.lexemeIdx=0},e.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var t=e.QueryParser.parseClause;t;)t=t(this);return this.query},e.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},e.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},e.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},e.QueryParser.parseClause=function(t){var r=t.peekLexeme();if(void 0!=r)switch(r.type){case e.QueryLexer.PRESENCE:return e.QueryParser.parsePresence;case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expected either a field or a term, found "+r.type;throw r.str.length>=1&&(i+=" with value '"+r.str+"'"),new e.QueryParseError(i,r.start,r.end)}},e.QueryParser.parsePresence=function(t){var r=t.consumeLexeme();if(void 0!=r){switch(r.str){case"-":t.currentClause.presence=e.Query.presence.PROHIBITED;break;case"+":t.currentClause.presence=e.Query.presence.REQUIRED;break;default:var i="unrecognised presence operator'"+r.str+"'";throw new e.QueryParseError(i,r.start,r.end)}var n=t.peekLexeme();if(void 0==n){var i="expecting term or field, found nothing";throw new e.QueryParseError(i,r.start,r.end)}switch(n.type){case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expecting term or field, found '"+n.type+"'";throw new e.QueryParseError(i,n.start,n.end)}}},e.QueryParser.parseField=function(t){var r=t.consumeLexeme();if(void 0!=r){if(t.query.allFields.indexOf(r.str)==-1){var i=t.query.allFields.map(function(e){return"'"+e+"'"}).join(", "),n="unrecognised field '"+r.str+"', possible fields: "+i;throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.fields=[r.str];var s=t.peekLexeme();if(void 0==s){var n="expecting term, found nothing";throw new e.QueryParseError(n,r.start,r.end)}switch(s.type){case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var n="expecting term, found '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseTerm=function(t){var r=t.consumeLexeme();if(void 0!=r){t.currentClause.term=r.str.toLowerCase(),r.str.indexOf("*")!=-1&&(t.currentClause.usePipeline=!1);var i=t.peekLexeme();if(void 0==i)return void t.nextClause();switch(i.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+i.type+"'";throw new e.QueryParseError(n,i.start,i.end)}}},e.QueryParser.parseEditDistance=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="edit distance must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.editDistance=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseBoost=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="boost must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.boost=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():e.lunr=t()}(this,function(){return e})}(); diff --git a/docs/1.8.1/AsyncHTTPClient/js/typeahead.jquery.js b/docs/1.8.1/AsyncHTTPClient/js/typeahead.jquery.js new file mode 100644 index 000000000..3a2d2ab03 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/js/typeahead.jquery.js @@ -0,0 +1,1694 @@ +/*! + * typeahead.js 1.3.1 + * https://github.com/corejavascript/typeahead.js + * Copyright 2013-2020 Twitter, Inc. and other contributors; Licensed MIT + */ + + +(function(root, factory) { + if (typeof define === "function" && define.amd) { + define([ "jquery" ], function(a0) { + return factory(a0); + }); + } else if (typeof module === "object" && module.exports) { + module.exports = factory(require("jquery")); + } else { + factory(root["jQuery"]); + } +})(this, function($) { + var _ = function() { + "use strict"; + return { + isMsie: function() { + return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false; + }, + isBlankString: function(str) { + return !str || /^\s*$/.test(str); + }, + escapeRegExChars: function(str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + }, + isString: function(obj) { + return typeof obj === "string"; + }, + isNumber: function(obj) { + return typeof obj === "number"; + }, + isArray: $.isArray, + isFunction: $.isFunction, + isObject: $.isPlainObject, + isUndefined: function(obj) { + return typeof obj === "undefined"; + }, + isElement: function(obj) { + return !!(obj && obj.nodeType === 1); + }, + isJQuery: function(obj) { + return obj instanceof $; + }, + toStr: function toStr(s) { + return _.isUndefined(s) || s === null ? "" : s + ""; + }, + bind: $.proxy, + each: function(collection, cb) { + $.each(collection, reverseArgs); + function reverseArgs(index, value) { + return cb(value, index); + } + }, + map: $.map, + filter: $.grep, + every: function(obj, test) { + var result = true; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (!(result = test.call(null, val, key, obj))) { + return false; + } + }); + return !!result; + }, + some: function(obj, test) { + var result = false; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (result = test.call(null, val, key, obj)) { + return false; + } + }); + return !!result; + }, + mixin: $.extend, + identity: function(x) { + return x; + }, + clone: function(obj) { + return $.extend(true, {}, obj); + }, + getIdGenerator: function() { + var counter = 0; + return function() { + return counter++; + }; + }, + templatify: function templatify(obj) { + return $.isFunction(obj) ? obj : template; + function template() { + return String(obj); + } + }, + defer: function(fn) { + setTimeout(fn, 0); + }, + debounce: function(func, wait, immediate) { + var timeout, result; + return function() { + var context = this, args = arguments, later, callNow; + later = function() { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + } + }; + callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + } + return result; + }; + }, + throttle: function(func, wait) { + var context, args, timeout, result, previous, later; + previous = 0; + later = function() { + previous = new Date(); + timeout = null; + result = func.apply(context, args); + }; + return function() { + var now = new Date(), remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + } else if (!timeout) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }, + stringify: function(val) { + return _.isString(val) ? val : JSON.stringify(val); + }, + guid: function() { + function _p8(s) { + var p = (Math.random().toString(16) + "000000000").substr(2, 8); + return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p; + } + return "tt-" + _p8() + _p8(true) + _p8(true) + _p8(); + }, + noop: function() {} + }; + }(); + var WWW = function() { + "use strict"; + var defaultClassNames = { + wrapper: "twitter-typeahead", + input: "tt-input", + hint: "tt-hint", + menu: "tt-menu", + dataset: "tt-dataset", + suggestion: "tt-suggestion", + selectable: "tt-selectable", + empty: "tt-empty", + open: "tt-open", + cursor: "tt-cursor", + highlight: "tt-highlight" + }; + return build; + function build(o) { + var www, classes; + classes = _.mixin({}, defaultClassNames, o); + www = { + css: buildCss(), + classes: classes, + html: buildHtml(classes), + selectors: buildSelectors(classes) + }; + return { + css: www.css, + html: www.html, + classes: www.classes, + selectors: www.selectors, + mixin: function(o) { + _.mixin(o, www); + } + }; + } + function buildHtml(c) { + return { + wrapper: '', + menu: '
' + }; + } + function buildSelectors(classes) { + var selectors = {}; + _.each(classes, function(v, k) { + selectors[k] = "." + v; + }); + return selectors; + } + function buildCss() { + var css = { + wrapper: { + position: "relative", + display: "inline-block" + }, + hint: { + position: "absolute", + top: "0", + left: "0", + borderColor: "transparent", + boxShadow: "none", + opacity: "1" + }, + input: { + position: "relative", + verticalAlign: "top", + backgroundColor: "transparent" + }, + inputWithNoHint: { + position: "relative", + verticalAlign: "top" + }, + menu: { + position: "absolute", + top: "100%", + left: "0", + zIndex: "100", + display: "none" + }, + ltr: { + left: "0", + right: "auto" + }, + rtl: { + left: "auto", + right: " 0" + } + }; + if (_.isMsie()) { + _.mixin(css.input, { + backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" + }); + } + return css; + } + }(); + var EventBus = function() { + "use strict"; + var namespace, deprecationMap; + namespace = "typeahead:"; + deprecationMap = { + render: "rendered", + cursorchange: "cursorchanged", + select: "selected", + autocomplete: "autocompleted" + }; + function EventBus(o) { + if (!o || !o.el) { + $.error("EventBus initialized without el"); + } + this.$el = $(o.el); + } + _.mixin(EventBus.prototype, { + _trigger: function(type, args) { + var $e = $.Event(namespace + type); + this.$el.trigger.call(this.$el, $e, args || []); + return $e; + }, + before: function(type) { + var args, $e; + args = [].slice.call(arguments, 1); + $e = this._trigger("before" + type, args); + return $e.isDefaultPrevented(); + }, + trigger: function(type) { + var deprecatedType; + this._trigger(type, [].slice.call(arguments, 1)); + if (deprecatedType = deprecationMap[type]) { + this._trigger(deprecatedType, [].slice.call(arguments, 1)); + } + } + }); + return EventBus; + }(); + var EventEmitter = function() { + "use strict"; + var splitter = /\s+/, nextTick = getNextTick(); + return { + onSync: onSync, + onAsync: onAsync, + off: off, + trigger: trigger + }; + function on(method, types, cb, context) { + var type; + if (!cb) { + return this; + } + types = types.split(splitter); + cb = context ? bindContext(cb, context) : cb; + this._callbacks = this._callbacks || {}; + while (type = types.shift()) { + this._callbacks[type] = this._callbacks[type] || { + sync: [], + async: [] + }; + this._callbacks[type][method].push(cb); + } + return this; + } + function onAsync(types, cb, context) { + return on.call(this, "async", types, cb, context); + } + function onSync(types, cb, context) { + return on.call(this, "sync", types, cb, context); + } + function off(types) { + var type; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + while (type = types.shift()) { + delete this._callbacks[type]; + } + return this; + } + function trigger(types) { + var type, callbacks, args, syncFlush, asyncFlush; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + args = [].slice.call(arguments, 1); + while ((type = types.shift()) && (callbacks = this._callbacks[type])) { + syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args)); + asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args)); + syncFlush() && nextTick(asyncFlush); + } + return this; + } + function getFlush(callbacks, context, args) { + return flush; + function flush() { + var cancelled; + for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) { + cancelled = callbacks[i].apply(context, args) === false; + } + return !cancelled; + } + } + function getNextTick() { + var nextTickFn; + if (window.setImmediate) { + nextTickFn = function nextTickSetImmediate(fn) { + setImmediate(function() { + fn(); + }); + }; + } else { + nextTickFn = function nextTickSetTimeout(fn) { + setTimeout(function() { + fn(); + }, 0); + }; + } + return nextTickFn; + } + function bindContext(fn, context) { + return fn.bind ? fn.bind(context) : function() { + fn.apply(context, [].slice.call(arguments, 0)); + }; + } + }(); + var highlight = function(doc) { + "use strict"; + var defaults = { + node: null, + pattern: null, + tagName: "strong", + className: null, + wordsOnly: false, + caseSensitive: false, + diacriticInsensitive: false + }; + var accented = { + A: "[AaªÀ-Åà-åĀ-ąǍǎȀ-ȃȦȧᴬᵃḀḁẚẠ-ảₐ℀℁℻⒜Ⓐⓐ㍱-㍴㎀-㎄㎈㎉㎩-㎯㏂㏊㏟㏿Aa]", + B: "[BbᴮᵇḂ-ḇℬ⒝Ⓑⓑ㍴㎅-㎇㏃㏈㏔㏝Bb]", + C: "[CcÇçĆ-čᶜ℀ℂ℃℅℆ℭⅭⅽ⒞Ⓒⓒ㍶㎈㎉㎝㎠㎤㏄-㏇Cc]", + D: "[DdĎďDŽ-džDZ-dzᴰᵈḊ-ḓⅅⅆⅮⅾ⒟Ⓓⓓ㋏㍲㍷-㍹㎗㎭-㎯㏅㏈Dd]", + E: "[EeÈ-Ëè-ëĒ-ěȄ-ȇȨȩᴱᵉḘ-ḛẸ-ẽₑ℡ℯℰⅇ⒠Ⓔⓔ㉐㋍㋎Ee]", + F: "[FfᶠḞḟ℉ℱ℻⒡Ⓕⓕ㎊-㎌㎙ff-fflFf]", + G: "[GgĜ-ģǦǧǴǵᴳᵍḠḡℊ⒢Ⓖⓖ㋌㋍㎇㎍-㎏㎓㎬㏆㏉㏒㏿Gg]", + H: "[HhĤĥȞȟʰᴴḢ-ḫẖℋ-ℎ⒣Ⓗⓗ㋌㍱㎐-㎔㏊㏋㏗Hh]", + I: "[IiÌ-Ïì-ïĨ-İIJijǏǐȈ-ȋᴵᵢḬḭỈ-ịⁱℐℑℹⅈⅠ-ⅣⅥ-ⅨⅪⅫⅰ-ⅳⅵ-ⅸⅺⅻ⒤Ⓘⓘ㍺㏌㏕fiffiIi]", + J: "[JjIJ-ĵLJ-njǰʲᴶⅉ⒥ⒿⓙⱼJj]", + K: "[KkĶķǨǩᴷᵏḰ-ḵK⒦Ⓚⓚ㎄㎅㎉㎏㎑㎘㎞㎢㎦㎪㎸㎾㏀㏆㏍-㏏Kk]", + L: "[LlĹ-ŀLJ-ljˡᴸḶḷḺ-ḽℒℓ℡Ⅼⅼ⒧Ⓛⓛ㋏㎈㎉㏐-㏓㏕㏖㏿flfflLl]", + M: "[MmᴹᵐḾ-ṃ℠™ℳⅯⅿ⒨Ⓜⓜ㍷-㍹㎃㎆㎎㎒㎖㎙-㎨㎫㎳㎷㎹㎽㎿㏁㏂㏎㏐㏔-㏖㏘㏙㏞㏟Mm]", + N: "[NnÑñŃ-ʼnNJ-njǸǹᴺṄ-ṋⁿℕ№⒩Ⓝⓝ㎁㎋㎚㎱㎵㎻㏌㏑Nn]", + O: "[OoºÒ-Öò-öŌ-őƠơǑǒǪǫȌ-ȏȮȯᴼᵒỌ-ỏₒ℅№ℴ⒪Ⓞⓞ㍵㏇㏒㏖Oo]", + P: "[PpᴾᵖṔ-ṗℙ⒫Ⓟⓟ㉐㍱㍶㎀㎊㎩-㎬㎰㎴㎺㏋㏗-㏚Pp]", + Q: "[Qqℚ⒬Ⓠⓠ㏃Qq]", + R: "[RrŔ-řȐ-ȓʳᴿᵣṘ-ṛṞṟ₨ℛ-ℝ⒭Ⓡⓡ㋍㍴㎭-㎯㏚㏛Rr]", + S: "[SsŚ-šſȘșˢṠ-ṣ₨℁℠⒮Ⓢⓢ㎧㎨㎮-㎳㏛㏜stSs]", + T: "[TtŢ-ťȚțᵀᵗṪ-ṱẗ℡™⒯Ⓣⓣ㉐㋏㎔㏏ſtstTt]", + U: "[UuÙ-Üù-üŨ-ųƯưǓǔȔ-ȗᵁᵘᵤṲ-ṷỤ-ủ℆⒰Ⓤⓤ㍳㍺Uu]", + V: "[VvᵛᵥṼ-ṿⅣ-Ⅷⅳ-ⅷ⒱Ⓥⓥⱽ㋎㍵㎴-㎹㏜㏞Vv]", + W: "[WwŴŵʷᵂẀ-ẉẘ⒲Ⓦⓦ㎺-㎿㏝Ww]", + X: "[XxˣẊ-ẍₓ℻Ⅸ-Ⅻⅸ-ⅻ⒳Ⓧⓧ㏓Xx]", + Y: "[YyÝýÿŶ-ŸȲȳʸẎẏẙỲ-ỹ⒴Ⓨⓨ㏉Yy]", + Z: "[ZzŹ-žDZ-dzᶻẐ-ẕℤℨ⒵Ⓩⓩ㎐-㎔Zz]" + }; + return function hightlight(o) { + var regex; + o = _.mixin({}, defaults, o); + if (!o.node || !o.pattern) { + return; + } + o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; + regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly, o.diacriticInsensitive); + traverse(o.node, hightlightTextNode); + function hightlightTextNode(textNode) { + var match, patternNode, wrapperNode; + if (match = regex.exec(textNode.data)) { + wrapperNode = doc.createElement(o.tagName); + o.className && (wrapperNode.className = o.className); + patternNode = textNode.splitText(match.index); + patternNode.splitText(match[0].length); + wrapperNode.appendChild(patternNode.cloneNode(true)); + textNode.parentNode.replaceChild(wrapperNode, patternNode); + } + return !!match; + } + function traverse(el, hightlightTextNode) { + var childNode, TEXT_NODE_TYPE = 3; + for (var i = 0; i < el.childNodes.length; i++) { + childNode = el.childNodes[i]; + if (childNode.nodeType === TEXT_NODE_TYPE) { + i += hightlightTextNode(childNode) ? 1 : 0; + } else { + traverse(childNode, hightlightTextNode); + } + } + } + }; + function accent_replacer(chr) { + return accented[chr.toUpperCase()] || chr; + } + function getRegex(patterns, caseSensitive, wordsOnly, diacriticInsensitive) { + var escapedPatterns = [], regexStr; + for (var i = 0, len = patterns.length; i < len; i++) { + var escapedWord = _.escapeRegExChars(patterns[i]); + if (diacriticInsensitive) { + escapedWord = escapedWord.replace(/\S/g, accent_replacer); + } + escapedPatterns.push(escapedWord); + } + regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")"; + return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); + } + }(window.document); + var Input = function() { + "use strict"; + var specialKeyCodeMap; + specialKeyCodeMap = { + 9: "tab", + 27: "esc", + 37: "left", + 39: "right", + 13: "enter", + 38: "up", + 40: "down" + }; + function Input(o, www) { + var id; + o = o || {}; + if (!o.input) { + $.error("input is missing"); + } + www.mixin(this); + this.$hint = $(o.hint); + this.$input = $(o.input); + this.$menu = $(o.menu); + id = this.$input.attr("id") || _.guid(); + this.$menu.attr("id", id + "_listbox"); + this.$hint.attr({ + "aria-hidden": true + }); + this.$input.attr({ + "aria-owns": id + "_listbox", + role: "combobox", + "aria-autocomplete": "list", + "aria-expanded": false + }); + this.query = this.$input.val(); + this.queryWhenFocused = this.hasFocus() ? this.query : null; + this.$overflowHelper = buildOverflowHelper(this.$input); + this._checkLanguageDirection(); + if (this.$hint.length === 0) { + this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; + } + this.onSync("cursorchange", this._updateDescendent); + } + Input.normalizeQuery = function(str) { + return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " "); + }; + _.mixin(Input.prototype, EventEmitter, { + _onBlur: function onBlur() { + this.resetInputValue(); + this.trigger("blurred"); + }, + _onFocus: function onFocus() { + this.queryWhenFocused = this.query; + this.trigger("focused"); + }, + _onKeydown: function onKeydown($e) { + var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; + this._managePreventDefault(keyName, $e); + if (keyName && this._shouldTrigger(keyName, $e)) { + this.trigger(keyName + "Keyed", $e); + } + }, + _onInput: function onInput() { + this._setQuery(this.getInputValue()); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + _managePreventDefault: function managePreventDefault(keyName, $e) { + var preventDefault; + switch (keyName) { + case "up": + case "down": + preventDefault = !withModifier($e); + break; + + default: + preventDefault = false; + } + preventDefault && $e.preventDefault(); + }, + _shouldTrigger: function shouldTrigger(keyName, $e) { + var trigger; + switch (keyName) { + case "tab": + trigger = !withModifier($e); + break; + + default: + trigger = true; + } + return trigger; + }, + _checkLanguageDirection: function checkLanguageDirection() { + var dir = (this.$input.css("direction") || "ltr").toLowerCase(); + if (this.dir !== dir) { + this.dir = dir; + this.$hint.attr("dir", dir); + this.trigger("langDirChanged", dir); + } + }, + _setQuery: function setQuery(val, silent) { + var areEquivalent, hasDifferentWhitespace; + areEquivalent = areQueriesEquivalent(val, this.query); + hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false; + this.query = val; + if (!silent && !areEquivalent) { + this.trigger("queryChanged", this.query); + } else if (!silent && hasDifferentWhitespace) { + this.trigger("whitespaceChanged", this.query); + } + }, + _updateDescendent: function updateDescendent(event, id) { + this.$input.attr("aria-activedescendant", id); + }, + bind: function() { + var that = this, onBlur, onFocus, onKeydown, onInput; + onBlur = _.bind(this._onBlur, this); + onFocus = _.bind(this._onFocus, this); + onKeydown = _.bind(this._onKeydown, this); + onInput = _.bind(this._onInput, this); + this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown); + if (!_.isMsie() || _.isMsie() > 9) { + this.$input.on("input.tt", onInput); + } else { + this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { + if (specialKeyCodeMap[$e.which || $e.keyCode]) { + return; + } + _.defer(_.bind(that._onInput, that, $e)); + }); + } + return this; + }, + focus: function focus() { + this.$input.focus(); + }, + blur: function blur() { + this.$input.blur(); + }, + getLangDir: function getLangDir() { + return this.dir; + }, + getQuery: function getQuery() { + return this.query || ""; + }, + setQuery: function setQuery(val, silent) { + this.setInputValue(val); + this._setQuery(val, silent); + }, + hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() { + return this.query !== this.queryWhenFocused; + }, + getInputValue: function getInputValue() { + return this.$input.val(); + }, + setInputValue: function setInputValue(value) { + this.$input.val(value); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + resetInputValue: function resetInputValue() { + this.setInputValue(this.query); + }, + getHint: function getHint() { + return this.$hint.val(); + }, + setHint: function setHint(value) { + this.$hint.val(value); + }, + clearHint: function clearHint() { + this.setHint(""); + }, + clearHintIfInvalid: function clearHintIfInvalid() { + var val, hint, valIsPrefixOfHint, isValid; + val = this.getInputValue(); + hint = this.getHint(); + valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; + isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow(); + !isValid && this.clearHint(); + }, + hasFocus: function hasFocus() { + return this.$input.is(":focus"); + }, + hasOverflow: function hasOverflow() { + var constraint = this.$input.width() - 2; + this.$overflowHelper.text(this.getInputValue()); + return this.$overflowHelper.width() >= constraint; + }, + isCursorAtEnd: function() { + var valueLength, selectionStart, range; + valueLength = this.$input.val().length; + selectionStart = this.$input[0].selectionStart; + if (_.isNumber(selectionStart)) { + return selectionStart === valueLength; + } else if (document.selection) { + range = document.selection.createRange(); + range.moveStart("character", -valueLength); + return valueLength === range.text.length; + } + return true; + }, + destroy: function destroy() { + this.$hint.off(".tt"); + this.$input.off(".tt"); + this.$overflowHelper.remove(); + this.$hint = this.$input = this.$overflowHelper = $("
"); + }, + setAriaExpanded: function setAriaExpanded(value) { + this.$input.attr("aria-expanded", value); + } + }); + return Input; + function buildOverflowHelper($input) { + return $('').css({ + position: "absolute", + visibility: "hidden", + whiteSpace: "pre", + fontFamily: $input.css("font-family"), + fontSize: $input.css("font-size"), + fontStyle: $input.css("font-style"), + fontVariant: $input.css("font-variant"), + fontWeight: $input.css("font-weight"), + wordSpacing: $input.css("word-spacing"), + letterSpacing: $input.css("letter-spacing"), + textIndent: $input.css("text-indent"), + textRendering: $input.css("text-rendering"), + textTransform: $input.css("text-transform") + }).insertAfter($input); + } + function areQueriesEquivalent(a, b) { + return Input.normalizeQuery(a) === Input.normalizeQuery(b); + } + function withModifier($e) { + return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; + } + }(); + var Dataset = function() { + "use strict"; + var keys, nameGenerator; + keys = { + dataset: "tt-selectable-dataset", + val: "tt-selectable-display", + obj: "tt-selectable-object" + }; + nameGenerator = _.getIdGenerator(); + function Dataset(o, www) { + o = o || {}; + o.templates = o.templates || {}; + o.templates.notFound = o.templates.notFound || o.templates.empty; + if (!o.source) { + $.error("missing source"); + } + if (!o.node) { + $.error("missing node"); + } + if (o.name && !isValidName(o.name)) { + $.error("invalid dataset name: " + o.name); + } + www.mixin(this); + this.highlight = !!o.highlight; + this.name = _.toStr(o.name || nameGenerator()); + this.limit = o.limit || 5; + this.displayFn = getDisplayFn(o.display || o.displayKey); + this.templates = getTemplates(o.templates, this.displayFn); + this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source; + this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async; + this._resetLastSuggestion(); + this.$el = $(o.node).attr("role", "presentation").addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); + } + Dataset.extractData = function extractData(el) { + var $el = $(el); + if ($el.data(keys.obj)) { + return { + dataset: $el.data(keys.dataset) || "", + val: $el.data(keys.val) || "", + obj: $el.data(keys.obj) || null + }; + } + return null; + }; + _.mixin(Dataset.prototype, EventEmitter, { + _overwrite: function overwrite(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (this.async && this.templates.pending) { + this._renderPending(query); + } else if (!this.async && this.templates.notFound) { + this._renderNotFound(query); + } else { + this._empty(); + } + this.trigger("rendered", suggestions, false, this.name); + }, + _append: function append(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length && this.$lastSuggestion.length) { + this._appendSuggestions(query, suggestions); + } else if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (!this.$lastSuggestion.length && this.templates.notFound) { + this._renderNotFound(query); + } + this.trigger("rendered", suggestions, true, this.name); + }, + _renderSuggestions: function renderSuggestions(query, suggestions) { + var $fragment; + $fragment = this._getSuggestionsFragment(query, suggestions); + this.$lastSuggestion = $fragment.children().last(); + this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions)); + }, + _appendSuggestions: function appendSuggestions(query, suggestions) { + var $fragment, $lastSuggestion; + $fragment = this._getSuggestionsFragment(query, suggestions); + $lastSuggestion = $fragment.children().last(); + this.$lastSuggestion.after($fragment); + this.$lastSuggestion = $lastSuggestion; + }, + _renderPending: function renderPending(query) { + var template = this.templates.pending; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _renderNotFound: function renderNotFound(query) { + var template = this.templates.notFound; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _empty: function empty() { + this.$el.empty(); + this._resetLastSuggestion(); + }, + _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) { + var that = this, fragment; + fragment = document.createDocumentFragment(); + _.each(suggestions, function getSuggestionNode(suggestion) { + var $el, context; + context = that._injectQuery(query, suggestion); + $el = $(that.templates.suggestion(context)).data(keys.dataset, that.name).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable); + fragment.appendChild($el[0]); + }); + this.highlight && highlight({ + className: this.classes.highlight, + node: fragment, + pattern: query + }); + return $(fragment); + }, + _getFooter: function getFooter(query, suggestions) { + return this.templates.footer ? this.templates.footer({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _getHeader: function getHeader(query, suggestions) { + return this.templates.header ? this.templates.header({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _resetLastSuggestion: function resetLastSuggestion() { + this.$lastSuggestion = $(); + }, + _injectQuery: function injectQuery(query, obj) { + return _.isObject(obj) ? _.mixin({ + _query: query + }, obj) : obj; + }, + update: function update(query) { + var that = this, canceled = false, syncCalled = false, rendered = 0; + this.cancel(); + this.cancel = function cancel() { + canceled = true; + that.cancel = $.noop; + that.async && that.trigger("asyncCanceled", query, that.name); + }; + this.source(query, sync, async); + !syncCalled && sync([]); + function sync(suggestions) { + if (syncCalled) { + return; + } + syncCalled = true; + suggestions = (suggestions || []).slice(0, that.limit); + rendered = suggestions.length; + that._overwrite(query, suggestions); + if (rendered < that.limit && that.async) { + that.trigger("asyncRequested", query, that.name); + } + } + function async(suggestions) { + suggestions = suggestions || []; + if (!canceled && rendered < that.limit) { + that.cancel = $.noop; + var idx = Math.abs(rendered - that.limit); + rendered += idx; + that._append(query, suggestions.slice(0, idx)); + that.async && that.trigger("asyncReceived", query, that.name); + } + } + }, + cancel: $.noop, + clear: function clear() { + this._empty(); + this.cancel(); + this.trigger("cleared"); + }, + isEmpty: function isEmpty() { + return this.$el.is(":empty"); + }, + destroy: function destroy() { + this.$el = $("
"); + } + }); + return Dataset; + function getDisplayFn(display) { + display = display || _.stringify; + return _.isFunction(display) ? display : displayFn; + function displayFn(obj) { + return obj[display]; + } + } + function getTemplates(templates, displayFn) { + return { + notFound: templates.notFound && _.templatify(templates.notFound), + pending: templates.pending && _.templatify(templates.pending), + header: templates.header && _.templatify(templates.header), + footer: templates.footer && _.templatify(templates.footer), + suggestion: templates.suggestion ? userSuggestionTemplate : suggestionTemplate + }; + function userSuggestionTemplate(context) { + var template = templates.suggestion; + return $(template(context)).attr("id", _.guid()); + } + function suggestionTemplate(context) { + return $('
').attr("id", _.guid()).text(displayFn(context)); + } + } + function isValidName(str) { + return /^[_a-zA-Z0-9-]+$/.test(str); + } + }(); + var Menu = function() { + "use strict"; + function Menu(o, www) { + var that = this; + o = o || {}; + if (!o.node) { + $.error("node is required"); + } + www.mixin(this); + this.$node = $(o.node); + this.query = null; + this.datasets = _.map(o.datasets, initializeDataset); + function initializeDataset(oDataset) { + var node = that.$node.find(oDataset.node).first(); + oDataset.node = node.length ? node : $("
").appendTo(that.$node); + return new Dataset(oDataset, www); + } + } + _.mixin(Menu.prototype, EventEmitter, { + _onSelectableClick: function onSelectableClick($e) { + this.trigger("selectableClicked", $($e.currentTarget)); + }, + _onRendered: function onRendered(type, dataset, suggestions, async) { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetRendered", dataset, suggestions, async); + }, + _onCleared: function onCleared() { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetCleared"); + }, + _propagate: function propagate() { + this.trigger.apply(this, arguments); + }, + _allDatasetsEmpty: function allDatasetsEmpty() { + return _.every(this.datasets, _.bind(function isDatasetEmpty(dataset) { + var isEmpty = dataset.isEmpty(); + this.$node.attr("aria-expanded", !isEmpty); + return isEmpty; + }, this)); + }, + _getSelectables: function getSelectables() { + return this.$node.find(this.selectors.selectable); + }, + _removeCursor: function _removeCursor() { + var $selectable = this.getActiveSelectable(); + $selectable && $selectable.removeClass(this.classes.cursor); + }, + _ensureVisible: function ensureVisible($el) { + var elTop, elBottom, nodeScrollTop, nodeHeight; + elTop = $el.position().top; + elBottom = elTop + $el.outerHeight(true); + nodeScrollTop = this.$node.scrollTop(); + nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10); + if (elTop < 0) { + this.$node.scrollTop(nodeScrollTop + elTop); + } else if (nodeHeight < elBottom) { + this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight)); + } + }, + bind: function() { + var that = this, onSelectableClick; + onSelectableClick = _.bind(this._onSelectableClick, this); + this.$node.on("click.tt", this.selectors.selectable, onSelectableClick); + this.$node.on("mouseover", this.selectors.selectable, function() { + that.setCursor($(this)); + }); + this.$node.on("mouseleave", function() { + that._removeCursor(); + }); + _.each(this.datasets, function(dataset) { + dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that); + }); + return this; + }, + isOpen: function isOpen() { + return this.$node.hasClass(this.classes.open); + }, + open: function open() { + this.$node.scrollTop(0); + this.$node.addClass(this.classes.open); + }, + close: function close() { + this.$node.attr("aria-expanded", false); + this.$node.removeClass(this.classes.open); + this._removeCursor(); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.attr("dir", dir); + }, + selectableRelativeToCursor: function selectableRelativeToCursor(delta) { + var $selectables, $oldCursor, oldIndex, newIndex; + $oldCursor = this.getActiveSelectable(); + $selectables = this._getSelectables(); + oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1; + newIndex = oldIndex + delta; + newIndex = (newIndex + 1) % ($selectables.length + 1) - 1; + newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex; + return newIndex === -1 ? null : $selectables.eq(newIndex); + }, + setCursor: function setCursor($selectable) { + this._removeCursor(); + if ($selectable = $selectable && $selectable.first()) { + $selectable.addClass(this.classes.cursor); + this._ensureVisible($selectable); + } + }, + getSelectableData: function getSelectableData($el) { + return $el && $el.length ? Dataset.extractData($el) : null; + }, + getActiveSelectable: function getActiveSelectable() { + var $selectable = this._getSelectables().filter(this.selectors.cursor).first(); + return $selectable.length ? $selectable : null; + }, + getTopSelectable: function getTopSelectable() { + var $selectable = this._getSelectables().first(); + return $selectable.length ? $selectable : null; + }, + update: function update(query) { + var isValidUpdate = query !== this.query; + if (isValidUpdate) { + this.query = query; + _.each(this.datasets, updateDataset); + } + return isValidUpdate; + function updateDataset(dataset) { + dataset.update(query); + } + }, + empty: function empty() { + _.each(this.datasets, clearDataset); + this.query = null; + this.$node.addClass(this.classes.empty); + function clearDataset(dataset) { + dataset.clear(); + } + }, + destroy: function destroy() { + this.$node.off(".tt"); + this.$node = $("
"); + _.each(this.datasets, destroyDataset); + function destroyDataset(dataset) { + dataset.destroy(); + } + } + }); + return Menu; + }(); + var Status = function() { + "use strict"; + function Status(options) { + this.$el = $("", { + role: "status", + "aria-live": "polite" + }).css({ + position: "absolute", + padding: "0", + border: "0", + height: "1px", + width: "1px", + "margin-bottom": "-1px", + "margin-right": "-1px", + overflow: "hidden", + clip: "rect(0 0 0 0)", + "white-space": "nowrap" + }); + options.$input.after(this.$el); + _.each(options.menu.datasets, _.bind(function(dataset) { + if (dataset.onSync) { + dataset.onSync("rendered", _.bind(this.update, this)); + dataset.onSync("cleared", _.bind(this.cleared, this)); + } + }, this)); + } + _.mixin(Status.prototype, { + update: function update(event, suggestions) { + var length = suggestions.length; + var words; + if (length === 1) { + words = { + result: "result", + is: "is" + }; + } else { + words = { + result: "results", + is: "are" + }; + } + this.$el.text(length + " " + words.result + " " + words.is + " available, use up and down arrow keys to navigate."); + }, + cleared: function() { + this.$el.text(""); + } + }); + return Status; + }(); + var DefaultMenu = function() { + "use strict"; + var s = Menu.prototype; + function DefaultMenu() { + Menu.apply(this, [].slice.call(arguments, 0)); + } + _.mixin(DefaultMenu.prototype, Menu.prototype, { + open: function open() { + !this._allDatasetsEmpty() && this._show(); + return s.open.apply(this, [].slice.call(arguments, 0)); + }, + close: function close() { + this._hide(); + return s.close.apply(this, [].slice.call(arguments, 0)); + }, + _onRendered: function onRendered() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onRendered.apply(this, [].slice.call(arguments, 0)); + }, + _onCleared: function onCleared() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onCleared.apply(this, [].slice.call(arguments, 0)); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl); + return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0)); + }, + _hide: function hide() { + this.$node.hide(); + }, + _show: function show() { + this.$node.css("display", "block"); + } + }); + return DefaultMenu; + }(); + var Typeahead = function() { + "use strict"; + function Typeahead(o, www) { + var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged; + o = o || {}; + if (!o.input) { + $.error("missing input"); + } + if (!o.menu) { + $.error("missing menu"); + } + if (!o.eventBus) { + $.error("missing event bus"); + } + www.mixin(this); + this.eventBus = o.eventBus; + this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; + this.input = o.input; + this.menu = o.menu; + this.enabled = true; + this.autoselect = !!o.autoselect; + this.active = false; + this.input.hasFocus() && this.activate(); + this.dir = this.input.getLangDir(); + this._hacks(); + this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this); + onFocused = c(this, "activate", "open", "_onFocused"); + onBlurred = c(this, "deactivate", "_onBlurred"); + onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed"); + onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed"); + onEscKeyed = c(this, "isActive", "_onEscKeyed"); + onUpKeyed = c(this, "isActive", "open", "_onUpKeyed"); + onDownKeyed = c(this, "isActive", "open", "_onDownKeyed"); + onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed"); + onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed"); + onQueryChanged = c(this, "_openIfActive", "_onQueryChanged"); + onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged"); + this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this); + } + _.mixin(Typeahead.prototype, { + _hacks: function hacks() { + var $input, $menu; + $input = this.input.$input || $("
"); + $menu = this.menu.$node || $("
"); + $input.on("blur.tt", function($e) { + var active, isActive, hasActive; + active = document.activeElement; + isActive = $menu.is(active); + hasActive = $menu.has(active).length > 0; + if (_.isMsie() && (isActive || hasActive)) { + $e.preventDefault(); + $e.stopImmediatePropagation(); + _.defer(function() { + $input.focus(); + }); + } + }); + $menu.on("mousedown.tt", function($e) { + $e.preventDefault(); + }); + }, + _onSelectableClicked: function onSelectableClicked(type, $el) { + this.select($el); + }, + _onDatasetCleared: function onDatasetCleared() { + this._updateHint(); + }, + _onDatasetRendered: function onDatasetRendered(type, suggestions, async, dataset) { + this._updateHint(); + if (this.autoselect) { + var cursorClass = this.selectors.cursor.substr(1); + this.menu.$node.find(this.selectors.suggestion).first().addClass(cursorClass); + } + this.eventBus.trigger("render", suggestions, async, dataset); + }, + _onAsyncRequested: function onAsyncRequested(type, dataset, query) { + this.eventBus.trigger("asyncrequest", query, dataset); + }, + _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) { + this.eventBus.trigger("asynccancel", query, dataset); + }, + _onAsyncReceived: function onAsyncReceived(type, dataset, query) { + this.eventBus.trigger("asyncreceive", query, dataset); + }, + _onFocused: function onFocused() { + this._minLengthMet() && this.menu.update(this.input.getQuery()); + }, + _onBlurred: function onBlurred() { + if (this.input.hasQueryChangedSinceLastFocus()) { + this.eventBus.trigger("change", this.input.getQuery()); + } + }, + _onEnterKeyed: function onEnterKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + if (this.select($selectable)) { + $e.preventDefault(); + $e.stopPropagation(); + } + } else if (this.autoselect) { + if (this.select(this.menu.getTopSelectable())) { + $e.preventDefault(); + $e.stopPropagation(); + } + } + }, + _onTabKeyed: function onTabKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + this.select($selectable) && $e.preventDefault(); + } else if (this.autoselect) { + if ($selectable = this.menu.getTopSelectable()) { + this.autocomplete($selectable) && $e.preventDefault(); + } + } + }, + _onEscKeyed: function onEscKeyed() { + this.close(); + }, + _onUpKeyed: function onUpKeyed() { + this.moveCursor(-1); + }, + _onDownKeyed: function onDownKeyed() { + this.moveCursor(+1); + }, + _onLeftKeyed: function onLeftKeyed() { + if (this.dir === "rtl" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); + } + }, + _onRightKeyed: function onRightKeyed() { + if (this.dir === "ltr" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); + } + }, + _onQueryChanged: function onQueryChanged(e, query) { + this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty(); + }, + _onWhitespaceChanged: function onWhitespaceChanged() { + this._updateHint(); + }, + _onLangDirChanged: function onLangDirChanged(e, dir) { + if (this.dir !== dir) { + this.dir = dir; + this.menu.setLanguageDirection(dir); + } + }, + _openIfActive: function openIfActive() { + this.isActive() && this.open(); + }, + _minLengthMet: function minLengthMet(query) { + query = _.isString(query) ? query : this.input.getQuery() || ""; + return query.length >= this.minLength; + }, + _updateHint: function updateHint() { + var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match; + $selectable = this.menu.getTopSelectable(); + data = this.menu.getSelectableData($selectable); + val = this.input.getInputValue(); + if (data && !_.isBlankString(val) && !this.input.hasOverflow()) { + query = Input.normalizeQuery(val); + escapedQuery = _.escapeRegExChars(query); + frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i"); + match = frontMatchRegEx.exec(data.val); + match && this.input.setHint(val + match[1]); + } else { + this.input.clearHint(); + } + }, + isEnabled: function isEnabled() { + return this.enabled; + }, + enable: function enable() { + this.enabled = true; + }, + disable: function disable() { + this.enabled = false; + }, + isActive: function isActive() { + return this.active; + }, + activate: function activate() { + if (this.isActive()) { + return true; + } else if (!this.isEnabled() || this.eventBus.before("active")) { + return false; + } else { + this.active = true; + this.eventBus.trigger("active"); + return true; + } + }, + deactivate: function deactivate() { + if (!this.isActive()) { + return true; + } else if (this.eventBus.before("idle")) { + return false; + } else { + this.active = false; + this.close(); + this.eventBus.trigger("idle"); + return true; + } + }, + isOpen: function isOpen() { + return this.menu.isOpen(); + }, + open: function open() { + if (!this.isOpen() && !this.eventBus.before("open")) { + this.input.setAriaExpanded(true); + this.menu.open(); + this._updateHint(); + this.eventBus.trigger("open"); + } + return this.isOpen(); + }, + close: function close() { + if (this.isOpen() && !this.eventBus.before("close")) { + this.input.setAriaExpanded(false); + this.menu.close(); + this.input.clearHint(); + this.input.resetInputValue(); + this.eventBus.trigger("close"); + } + return !this.isOpen(); + }, + setVal: function setVal(val) { + this.input.setQuery(_.toStr(val)); + }, + getVal: function getVal() { + return this.input.getQuery(); + }, + select: function select($selectable) { + var data = this.menu.getSelectableData($selectable); + if (data && !this.eventBus.before("select", data.obj, data.dataset)) { + this.input.setQuery(data.val, true); + this.eventBus.trigger("select", data.obj, data.dataset); + this.close(); + return true; + } + return false; + }, + autocomplete: function autocomplete($selectable) { + var query, data, isValid; + query = this.input.getQuery(); + data = this.menu.getSelectableData($selectable); + isValid = data && query !== data.val; + if (isValid && !this.eventBus.before("autocomplete", data.obj, data.dataset)) { + this.input.setQuery(data.val); + this.eventBus.trigger("autocomplete", data.obj, data.dataset); + return true; + } + return false; + }, + moveCursor: function moveCursor(delta) { + var query, $candidate, data, suggestion, datasetName, cancelMove, id; + query = this.input.getQuery(); + $candidate = this.menu.selectableRelativeToCursor(delta); + data = this.menu.getSelectableData($candidate); + suggestion = data ? data.obj : null; + datasetName = data ? data.dataset : null; + id = $candidate ? $candidate.attr("id") : null; + this.input.trigger("cursorchange", id); + cancelMove = this._minLengthMet() && this.menu.update(query); + if (!cancelMove && !this.eventBus.before("cursorchange", suggestion, datasetName)) { + this.menu.setCursor($candidate); + if (data) { + if (typeof data.val === "string") { + this.input.setInputValue(data.val); + } + } else { + this.input.resetInputValue(); + this._updateHint(); + } + this.eventBus.trigger("cursorchange", suggestion, datasetName); + return true; + } + return false; + }, + destroy: function destroy() { + this.input.destroy(); + this.menu.destroy(); + } + }); + return Typeahead; + function c(ctx) { + var methods = [].slice.call(arguments, 1); + return function() { + var args = [].slice.call(arguments); + _.each(methods, function(method) { + return ctx[method].apply(ctx, args); + }); + }; + } + }(); + (function() { + "use strict"; + var old, keys, methods; + old = $.fn.typeahead; + keys = { + www: "tt-www", + attrs: "tt-attrs", + typeahead: "tt-typeahead" + }; + methods = { + initialize: function initialize(o, datasets) { + var www; + datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); + o = o || {}; + www = WWW(o.classNames); + return this.each(attach); + function attach() { + var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, status, typeahead, MenuConstructor; + _.each(datasets, function(d) { + d.highlight = !!o.highlight; + }); + $input = $(this); + $wrapper = $(www.html.wrapper); + $hint = $elOrNull(o.hint); + $menu = $elOrNull(o.menu); + defaultHint = o.hint !== false && !$hint; + defaultMenu = o.menu !== false && !$menu; + defaultHint && ($hint = buildHintFromInput($input, www)); + defaultMenu && ($menu = $(www.html.menu).css(www.css.menu)); + $hint && $hint.val(""); + $input = prepInput($input, www); + if (defaultHint || defaultMenu) { + $wrapper.css(www.css.wrapper); + $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint); + $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null); + } + MenuConstructor = defaultMenu ? DefaultMenu : Menu; + eventBus = new EventBus({ + el: $input + }); + input = new Input({ + hint: $hint, + input: $input, + menu: $menu + }, www); + menu = new MenuConstructor({ + node: $menu, + datasets: datasets + }, www); + status = new Status({ + $input: $input, + menu: menu + }); + typeahead = new Typeahead({ + input: input, + menu: menu, + eventBus: eventBus, + minLength: o.minLength, + autoselect: o.autoselect + }, www); + $input.data(keys.www, www); + $input.data(keys.typeahead, typeahead); + } + }, + isEnabled: function isEnabled() { + var enabled; + ttEach(this.first(), function(t) { + enabled = t.isEnabled(); + }); + return enabled; + }, + enable: function enable() { + ttEach(this, function(t) { + t.enable(); + }); + return this; + }, + disable: function disable() { + ttEach(this, function(t) { + t.disable(); + }); + return this; + }, + isActive: function isActive() { + var active; + ttEach(this.first(), function(t) { + active = t.isActive(); + }); + return active; + }, + activate: function activate() { + ttEach(this, function(t) { + t.activate(); + }); + return this; + }, + deactivate: function deactivate() { + ttEach(this, function(t) { + t.deactivate(); + }); + return this; + }, + isOpen: function isOpen() { + var open; + ttEach(this.first(), function(t) { + open = t.isOpen(); + }); + return open; + }, + open: function open() { + ttEach(this, function(t) { + t.open(); + }); + return this; + }, + close: function close() { + ttEach(this, function(t) { + t.close(); + }); + return this; + }, + select: function select(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.select($el); + }); + return success; + }, + autocomplete: function autocomplete(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.autocomplete($el); + }); + return success; + }, + moveCursor: function moveCursoe(delta) { + var success = false; + ttEach(this.first(), function(t) { + success = t.moveCursor(delta); + }); + return success; + }, + val: function val(newVal) { + var query; + if (!arguments.length) { + ttEach(this.first(), function(t) { + query = t.getVal(); + }); + return query; + } else { + ttEach(this, function(t) { + t.setVal(_.toStr(newVal)); + }); + return this; + } + }, + destroy: function destroy() { + ttEach(this, function(typeahead, $input) { + revert($input); + typeahead.destroy(); + }); + return this; + } + }; + $.fn.typeahead = function(method) { + if (methods[method]) { + return methods[method].apply(this, [].slice.call(arguments, 1)); + } else { + return methods.initialize.apply(this, arguments); + } + }; + $.fn.typeahead.noConflict = function noConflict() { + $.fn.typeahead = old; + return this; + }; + function ttEach($els, fn) { + $els.each(function() { + var $input = $(this), typeahead; + (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input); + }); + } + function buildHintFromInput($input, www) { + return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop({ + readonly: true, + required: false + }).removeAttr("id name placeholder").removeClass("required").attr({ + spellcheck: "false", + tabindex: -1 + }); + } + function prepInput($input, www) { + $input.data(keys.attrs, { + dir: $input.attr("dir"), + autocomplete: $input.attr("autocomplete"), + spellcheck: $input.attr("spellcheck"), + style: $input.attr("style") + }); + $input.addClass(www.classes.input).attr({ + spellcheck: false + }); + try { + !$input.attr("dir") && $input.attr("dir", "auto"); + } catch (e) {} + return $input; + } + function getBackgroundStyles($el) { + return { + backgroundAttachment: $el.css("background-attachment"), + backgroundClip: $el.css("background-clip"), + backgroundColor: $el.css("background-color"), + backgroundImage: $el.css("background-image"), + backgroundOrigin: $el.css("background-origin"), + backgroundPosition: $el.css("background-position"), + backgroundRepeat: $el.css("background-repeat"), + backgroundSize: $el.css("background-size") + }; + } + function revert($input) { + var www, $wrapper; + www = $input.data(keys.www); + $wrapper = $input.parent().filter(www.selectors.wrapper); + _.each($input.data(keys.attrs), function(val, key) { + _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); + }); + $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input); + if ($wrapper.length) { + $input.detach().insertAfter($wrapper); + $wrapper.remove(); + } + } + function $elOrNull(obj) { + var isValid, $el; + isValid = _.isJQuery(obj) || _.isElement(obj); + $el = isValid ? $(obj).first() : []; + return $el.length ? $el : null; + } + })(); +}); \ No newline at end of file diff --git a/docs/1.8.1/AsyncHTTPClient/search.json b/docs/1.8.1/AsyncHTTPClient/search.json new file mode 100644 index 000000000..ad96bd7d6 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/search.json @@ -0,0 +1 @@ +{"Structs/HTTPClientError.html#/s:s23CustomStringConvertibleP11descriptionSSvp":{"name":"description","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV10invalidURLACvpZ":{"name":"invalidURL","abstract":"

URL provided is invalid.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV9emptyHostACvpZ":{"name":"emptyHost","abstract":"

URL does not contain host.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV17missingSocketPathACvpZ":{"name":"missingSocketPath","abstract":"

URL does not contain a socketPath as a host for http(s)+unix shemes.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV15alreadyShutdownACvpZ":{"name":"alreadyShutdown","abstract":"

Client is shutdown and cannot be used for new requests.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV11emptySchemeACvpZ":{"name":"emptyScheme","abstract":"

URL does not contain scheme.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV17unsupportedSchemeyACSSFZ":{"name":"unsupportedScheme(_:)","abstract":"

Provided URL scheme is not supported, supported schemes are: http and https

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV11readTimeoutACvpZ":{"name":"readTimeout","abstract":"

Request timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV22remoteConnectionClosedACvpZ":{"name":"remoteConnectionClosed","abstract":"

Remote connection was closed unexpectedly.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV9cancelledACvpZ":{"name":"cancelled","abstract":"

Request was cancelled.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV32identityCodingIncorrectlyPresentACvpZ":{"name":"identityCodingIncorrectlyPresent","abstract":"

Request contains invalid identity encoding.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV29chunkedSpecifiedMultipleTimesACvpZ":{"name":"chunkedSpecifiedMultipleTimes","abstract":"

Request contains multiple chunks definitions.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20invalidProxyResponseACvpZ":{"name":"invalidProxyResponse","abstract":"

Proxy response was invalid.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20contentLengthMissingACvpZ":{"name":"contentLengthMissing","abstract":"

Request does not contain Content-Length header.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV27proxyAuthenticationRequiredACvpZ":{"name":"proxyAuthenticationRequired","abstract":"

Proxy Authentication Required.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20redirectLimitReachedACvpZ":{"name":"redirectLimitReached","abstract":"

Redirect Limit reached.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV21redirectCycleDetectedACvpZ":{"name":"redirectCycleDetected","abstract":"

Redirect Cycle detected.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV15uncleanShutdownACvpZ":{"name":"uncleanShutdown","abstract":"

Unclean shutdown.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20traceRequestWithBodyACvpZ":{"name":"traceRequestWithBody","abstract":"

A body was sent in a request with method TRACE.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV23invalidHeaderFieldNamesyACSaySSGFZ":{"name":"invalidHeaderFieldNames(_:)","abstract":"

Header field names contain invalid characters.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV18bodyLengthMismatchACvpZ":{"name":"bodyLengthMismatch","abstract":"

Body length is not equal to Content-Length.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV21writeAfterRequestSentACvpZ":{"name":"writeAfterRequestSent","abstract":"

Body part was written after request was fully sent.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV19incompatibleHeadersACvpZ":{"name":"incompatibleHeaders","abstract":"

Incompatible headers specified, for example Transfer-Encoding and Content-Length.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV14connectTimeoutACvpZ":{"name":"connectTimeout","abstract":"

Creating a new tcp connection timed out

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV21socksHandshakeTimeoutACvpZ":{"name":"socksHandshakeTimeout","abstract":"

The socks handshake timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV25httpProxyHandshakeTimeoutACvpZ":{"name":"httpProxyHandshakeTimeout","abstract":"

The http proxy connection creation timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV19tlsHandshakeTimeoutACvpZ":{"name":"tlsHandshakeTimeout","abstract":"

The tls handshake timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV43serverOfferedUnsupportedApplicationProtocolyACSSFZ":{"name":"serverOfferedUnsupportedApplicationProtocol(_:)","abstract":"

The remote server only offered an unsupported application protocol

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV16deadlineExceededACvpZ":{"name":"deadlineExceeded","abstract":"

The request deadline was exceeded. The request was cancelled because of this.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV22requestStreamCancelledACvpZ":{"name":"requestStreamCancelled","abstract":"

The remote server responded with a status code >= 300, before the full request was sent. The request stream","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV28getConnectionFromPoolTimeoutACvpZ":{"name":"getConnectionFromPoolTimeout","abstract":"

Aquiring a HTTP connection from the connection pool timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV31httpEndReceivedAfterHeadWith1xxACvpZ":{"name":"httpEndReceivedAfterHeadWith1xx","abstract":"

Undocumented

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html":{"name":"HTTPClientError","abstract":"

Possible client errors.

"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP0C0Qa":{"name":"Response","abstract":"

Undocumented

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didSendRequestHead4task_yAA0B0C4TaskCy_0C0QzG_8NIOHTTP1011HTTPRequestH0VtF":{"name":"didSendRequestHead(task:_:)","abstract":"

Called when the request head is sent. Will be called once.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didSendRequestPart4task_yAA0B0C4TaskCy_0C0QzG_7NIOCore6IODataOtF":{"name":"didSendRequestPart(task:_:)","abstract":"

Called when a part of the request body is sent. Could be called zero or more times.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didSendRequest4taskyAA0B0C4TaskCy_0C0QzG_tF":{"name":"didSendRequest(task:)","abstract":"

Called when the request is fully sent. Will be called once.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","abstract":"

Called when response head is received. Will be called once.","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","abstract":"

Called when part of a response body is received. Could be called zero or more times.","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","abstract":"

Called when error was thrown during request execution. Will be called zero or one time only. Request processing will be stopped after that.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","abstract":"

Called when the complete HTTP request is finished. You must return an instance of your Response associated type. Will be called once, except if an error occurred.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html":{"name":"HTTPClientResponseDelegate","abstract":"

HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed."},"Extensions/URL.html#/s:10Foundation3URLV15AsyncHTTPClientE21httpURLWithSocketPath3uriACSgSS_SStcfc":{"name":"init(httpURLWithSocketPath:uri:)","abstract":"

Initializes a newly created HTTP URL connecting to a unix domain socket path. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “http+unix” scheme.

","parent_name":"URL"},"Extensions/URL.html#/s:10Foundation3URLV15AsyncHTTPClientE22httpsURLWithSocketPath3uriACSgSS_SStcfc":{"name":"init(httpsURLWithSocketPath:uri:)","abstract":"

Initializes a newly created HTTPS URL connecting to a unix domain socket path over TLS. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “https+unix” scheme.

","parent_name":"URL"},"Extensions/URL.html":{"name":"URL"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B15CopyingDelegateC8Responsea":{"name":"Response","abstract":"

Undocumented

","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B15CopyingDelegateC12chunkHandlerAC7NIOCore15EventLoopFutureCyytGAE10ByteBufferVc_tcfc":{"name":"init(chunkHandler:)","abstract":"

Undocumented

","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"HTTPClientCopyingDelegate"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient19ResponseAccumulatorC0C0a":{"name":"Response","abstract":"

Undocumented

","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient19ResponseAccumulatorC7requestAcA0B0C7RequestV_tcfc":{"name":"init(request:)","abstract":"

Undocumented

","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"ResponseAccumulator"},"Classes/HTTPClient/NWTLSError.html#/status":{"name":"status","abstract":"

TLS error status. List of TLS errors can be found in

","parent_name":"NWTLSError"},"Classes/HTTPClient/NWTLSError.html#/init(_:reason:)":{"name":"init(_:reason:)","abstract":"

initialise a NWTLSError

","parent_name":"NWTLSError"},"Classes/HTTPClient/NWTLSError.html#/description":{"name":"description","parent_name":"NWTLSError"},"Classes/HTTPClient/NWPOSIXError.html#/errorCode":{"name":"errorCode","abstract":"

POSIX error code (enum)

","parent_name":"NWPOSIXError"},"Classes/HTTPClient/NWPOSIXError.html#/init(_:reason:)":{"name":"init(_:reason:)","abstract":"

Initialise a NWPOSIXError

","parent_name":"NWPOSIXError"},"Classes/HTTPClient/NWPOSIXError.html#/description":{"name":"description","parent_name":"NWPOSIXError"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC9eventLoop7NIOCore05EventE0_pvp":{"name":"eventLoop","abstract":"

The EventLoop the delegate will be executed on.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC12futureResult7NIOCore15EventLoopFutureCyxGvp":{"name":"futureResult","abstract":"

EventLoopFuture for the response returned by this request.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC4waitxyKF":{"name":"wait()","abstract":"

Waits for execution of this request to complete.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC6cancelyyF":{"name":"cancel()","abstract":"

Cancels the request execution.

","parent_name":"Task"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV5basic8username8passwordAESS_SStFZ":{"name":"basic(username:password:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV5basic11credentialsAESS_tFZ":{"name":"basic(credentials:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV6bearer6tokensAESS_tFZ":{"name":"bearer(tokens:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV11headerValueSSvp":{"name":"headerValue","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6method8NIOHTTP110HTTPMethodOvp":{"name":"method","abstract":"

Request HTTP method, defaults to GET.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url10Foundation3URLVvp":{"name":"url","abstract":"

Remote URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6schemeSSvp":{"name":"scheme","abstract":"

Remote HTTP scheme, resolved from URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV7headers8NIOHTTP111HTTPHeadersVvp":{"name":"headers","abstract":"

Request custom HTTP Headers, defaults to no headers.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4bodyAC4BodyVSgvp":{"name":"body","abstract":"

Request body, defaults to no body.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV16tlsConfiguration6NIOSSL16TLSConfigurationVSgvp":{"name":"tlsConfiguration","abstract":"

Request-specific TLS configuration, defaults to no request-specific TLS configuration.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4bodyAESS_8NIOHTTP110HTTPMethodOAJ11HTTPHeadersVAC4BodyVSgtKcfc":{"name":"init(url:method:headers:body:)","abstract":"

Create HTTP request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4body16tlsConfigurationAESS_8NIOHTTP110HTTPMethodOAK11HTTPHeadersVAC4BodyVSg6NIOSSL16TLSConfigurationVSgtKcfc":{"name":"init(url:method:headers:body:tlsConfiguration:)","abstract":"

Create HTTP request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4bodyAE10Foundation3URLV_8NIOHTTP110HTTPMethodOAM11HTTPHeadersVAC4BodyVSgtKcfc":{"name":"init(url:method:headers:body:)","abstract":"

Create an HTTP Request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4body16tlsConfigurationAE10Foundation3URLV_8NIOHTTP110HTTPMethodOAN11HTTPHeadersVAC4BodyVSg6NIOSSL16TLSConfigurationVSgtKcfc":{"name":"init(url:method:headers:body:tlsConfiguration:)","abstract":"

Create an HTTP Request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4hostSSvp":{"name":"host","abstract":"

Remote host, resolved from URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4portSivp":{"name":"port","abstract":"

Resolved port.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6useTLSSbvp":{"name":"useTLS","abstract":"

Whether request will be executed using secure socket.

","parent_name":"Request"},"Classes/HTTPClient/Body/StreamWriter.html#/s:15AsyncHTTPClient0B0C4BodyV12StreamWriterV7closureAG7NIOCore15EventLoopFutureCyytGAI6IODataOc_tcfc":{"name":"init(closure:)","abstract":"

Create new StreamWriter

","parent_name":"StreamWriter"},"Classes/HTTPClient/Body/StreamWriter.html#/s:15AsyncHTTPClient0B0C4BodyV12StreamWriterV5writey7NIOCore15EventLoopFutureCyytGAI6IODataOF":{"name":"write(_:)","abstract":"

Write data to server.

","parent_name":"StreamWriter"},"Classes/HTTPClient/Body/StreamWriter.html":{"name":"StreamWriter","abstract":"

Chunk provider.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6lengthSiSgvp":{"name":"length","abstract":"

Body size. if nil,Transfer-Encoding will automatically be set to chunked. Otherwise a Content-Length","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6streamy7NIOCore15EventLoopFutureCyytGAE12StreamWriterVcvp":{"name":"stream","abstract":"

Body chunk provider.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV10byteBufferyAE7NIOCore04ByteE0VFZ":{"name":"byteBuffer(_:)","abstract":"

Create and stream body using ByteBuffer.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6stream6length_AESiSg_7NIOCore15EventLoopFutureCyytGAE12StreamWriterVctFZ":{"name":"stream(length:_:)","abstract":"

Create and stream body using StreamWriter.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV4datayAE10Foundation4DataVFZ":{"name":"data(_:)","abstract":"

Create and stream body using Data.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6stringyAESSFZ":{"name":"string(_:)","abstract":"

Create and stream body using String.

","parent_name":"Body"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4hostSSvp":{"name":"host","abstract":"

Remote host of the request.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV6status8NIOHTTP118HTTPResponseStatusOvp":{"name":"status","abstract":"

Response HTTP status.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7version8NIOHTTP111HTTPVersionVvp":{"name":"version","abstract":"

Response HTTP version.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7headers8NIOHTTP111HTTPHeadersVvp":{"name":"headers","abstract":"

Reponse HTTP headers.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4body7NIOCore10ByteBufferVSgvp":{"name":"body","abstract":"

Response body.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4host6status7headers4bodyAESS_8NIOHTTP118HTTPResponseStatusOAJ11HTTPHeadersV7NIOCore10ByteBufferVSgtcfc":{"name":"init(host:status:headers:body:)","abstract":"

Create HTTP Response.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4host6status7version7headers4bodyAESS_8NIOHTTP118HTTPResponseStatusOAK11HTTPVersionVAK11HTTPHeadersV7NIOCore10ByteBufferVSgtcfc":{"name":"init(host:status:version:headers:body:)","abstract":"

Create HTTP Response.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7cookiesSayAC6CookieVGvp":{"name":"cookies","abstract":"

List of HTTP cookies returned by the server.

","parent_name":"Response"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4nameSSvp":{"name":"name","abstract":"

The name of the cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV5valueSSvp":{"name":"value","abstract":"

The cookie’s string value.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4pathSSvp":{"name":"path","abstract":"

The cookie’s path.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6domainSSSgvp":{"name":"domain","abstract":"

The domain of the cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV7expires10Foundation4DateVSgvp":{"name":"expires","abstract":"

The cookie’s expiration date.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6maxAgeSiSgvp":{"name":"maxAge","abstract":"

The cookie’s age in seconds.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV8httpOnlySbvp":{"name":"httpOnly","abstract":"

Whether the cookie should only be sent to HTTP servers.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6secureSbvp":{"name":"secure","abstract":"

Whether the cookie should only be sent over secure channels.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6header13defaultDomainAESgSS_SStcfc":{"name":"init(header:defaultDomain:)","abstract":"

Create a Cookie by parsing a Set-Cookie header.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4name5value4path6domain7expires6maxAge8httpOnly6secureAESS_S3SSg10Foundation4DateVSgSiSgS2btcfc":{"name":"init(name:value:path:domain:expires:maxAge:httpOnly:secure:)","abstract":"

Create HTTP cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Decompression.html#/s:15AsyncHTTPClient0B0C13DecompressionO8disabledyA2EmF":{"name":"disabled","abstract":"

Decompression is disabled.

","parent_name":"Decompression"},"Classes/HTTPClient/Decompression.html#/s:15AsyncHTTPClient0B0C13DecompressionO7enabledyAE18NIOHTTPCompression20NIOHTTPDecompressionO0C5LimitV_tcAEmF":{"name":"enabled(limit:)","abstract":"

Decompression is enabled.

","parent_name":"Decompression"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV11indifferentAEvpZ":{"name":"indifferent","abstract":"

Event Loop will be selected by the library.

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV8delegate2onAE7NIOCore0cD0_p_tFZ":{"name":"delegate(on:)","abstract":"

The delegate will be run on the specified EventLoop (and the Channel if possible).

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV18delegateAndChannel2onAE7NIOCore0cD0_p_tFZ":{"name":"delegateAndChannel(on:)","abstract":"

The delegate and the Channel will be run on the specified EventLoop.

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:s23CustomStringConvertibleP11descriptionSSvp":{"name":"description","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopGroupProvider.html#/s:15AsyncHTTPClient0B0C22EventLoopGroupProviderO6sharedyAE7NIOCore0cdE0_pcAEmF":{"name":"shared(_:)","abstract":"

EventLoopGroup will be provided by the user. Owner of this group is responsible for its lifecycle.

","parent_name":"EventLoopGroupProvider"},"Classes/HTTPClient/EventLoopGroupProvider.html#/s:15AsyncHTTPClient0B0C22EventLoopGroupProviderO9createNewyA2EmF":{"name":"createNew","abstract":"

EventLoopGroup will be created by the client. When syncShutdown is called, created EventLoopGroup will be shut down as well.

","parent_name":"EventLoopGroupProvider"},"Classes/HTTPClient/Configuration/HTTPVersion.html#/s:15AsyncHTTPClient0B0C13ConfigurationV11HTTPVersionV9http1OnlyAGvpZ":{"name":"http1Only","abstract":"

we only use HTTP/1, even if the server would supports HTTP/2

","parent_name":"HTTPVersion"},"Classes/HTTPClient/Configuration/HTTPVersion.html#/s:15AsyncHTTPClient0B0C13ConfigurationV11HTTPVersionV9automaticAGvpZ":{"name":"automatic","abstract":"

HTTP/2 is used if we connect to a server with HTTPS and the server supports HTTP/2, otherwise we use HTTP/1

","parent_name":"HTTPVersion"},"Classes/HTTPClient/Configuration/ConnectionPool.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14ConnectionPoolV11idleTimeout7NIOCore10TimeAmountVvp":{"name":"idleTimeout","abstract":"

Specifies amount of time connections are kept idle in the pool. After this time has passed without a new","parent_name":"ConnectionPool"},"Classes/HTTPClient/Configuration/ConnectionPool.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14ConnectionPoolV42concurrentHTTP1ConnectionsPerHostSoftLimitSivp":{"name":"concurrentHTTP1ConnectionsPerHostSoftLimit","abstract":"

The maximum number of connections that are kept alive in the connection pool per host. If requests with","parent_name":"ConnectionPool"},"Classes/HTTPClient/Configuration/ConnectionPool.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14ConnectionPoolV11idleTimeoutAG7NIOCore10TimeAmountV_tcfc":{"name":"init(idleTimeout:)","abstract":"

Undocumented

","parent_name":"ConnectionPool"},"Classes/HTTPClient/Configuration/ConnectionPool.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14ConnectionPoolV11idleTimeout42concurrentHTTP1ConnectionsPerHostSoftLimitAG7NIOCore10TimeAmountV_Sitcfc":{"name":"init(idleTimeout:concurrentHTTP1ConnectionsPerHostSoftLimit:)","abstract":"

Undocumented

","parent_name":"ConnectionPool"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08RedirectC0V8disallowAGvpZ":{"name":"disallow","abstract":"

Redirects are not followed.

","parent_name":"RedirectConfiguration"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08RedirectC0V6follow3max11allowCyclesAGSi_SbtFZ":{"name":"follow(max:allowCycles:)","abstract":"

Redirects are followed with a specified limit.

","parent_name":"RedirectConfiguration"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV7connect7NIOCore10TimeAmountVSgvp":{"name":"connect","abstract":"

Specifies connect timeout. If no connect timeout is given, a default 30 seconds timeout will applied.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV4read7NIOCore10TimeAmountVSgvp":{"name":"read","abstract":"

Specifies read timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV7connect4readAG7NIOCore10TimeAmountVSg_AMtcfc":{"name":"init(connect:read:)","abstract":"

Create timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV4hostSSvp":{"name":"host","abstract":"

Specifies Proxy server host.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV4portSivp":{"name":"port","abstract":"

Specifies Proxy server port.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV13authorizationAC13AuthorizationVSgvp":{"name":"authorization","abstract":"

Specifies Proxy server authorization.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV6server4host4portAGSS_SitFZ":{"name":"server(host:port:)","abstract":"

Create a HTTP proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV6server4host4port13authorizationAGSS_SiAC13AuthorizationVSgtFZ":{"name":"server(host:port:authorization:)","abstract":"

Create a HTTP proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV11socksServer4host4portAGSS_SitFZ":{"name":"socksServer(host:port:)","abstract":"

Create a SOCKSv5 proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC06NIOSSL16TLSConfigurationVSgvp":{"name":"tlsConfiguration","abstract":"

TLS configuration, defaults to TLSConfiguration.makeClientConfiguration().

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08redirectC0AE08RedirectC0Vvp":{"name":"redirectConfiguration","abstract":"

Enables following 3xx redirects automatically, defaults to RedirectConfiguration().

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7timeoutAE7TimeoutVvp":{"name":"timeout","abstract":"

Default client timeout, defaults to no read timeout and 10 seconds connect timeout.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14connectionPoolAE010ConnectionE0Vvp":{"name":"connectionPool","abstract":"

Connection pool configuration.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5proxyAE5ProxyVSgvp":{"name":"proxy","abstract":"

Upstream proxy, defaults to no proxy.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV13decompressionAC13DecompressionOvp":{"name":"decompression","abstract":"

Enables automatic body decompression. Supported algorithms are gzip and deflate.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV24ignoreUncleanSSLShutdownSbvp":{"name":"ignoreUncleanSSLShutdown","abstract":"

Ignore TLS unclean shutdown error, defaults to false.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV11httpVersionAE11HTTPVersionVvp":{"name":"httpVersion","abstract":"

is set to .automatic by default which will use HTTP/2 if run over https and the server supports it, otherwise HTTP/1

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC008redirectC07timeout14connectionPool5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL16TLSConfigurationVSg_AE08RedirectC0VSgAE7TimeoutVAE010ConnectionH0VAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(tlsConfiguration:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC008redirectC07timeout5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL16TLSConfigurationVSg_AE08RedirectC0VSgAE7TimeoutVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(tlsConfiguration:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout38maximumAllowedIdleTimeInConnectionPool5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutV7NIOCore0K6AmountVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:maximumAllowedIdleTimeInConnectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout14connectionPool5proxy24ignoreUncleanSSLShutdown13decompression24backgroundActivityLoggerAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutV7NIOCore10TimeAmountVAE5ProxyVSgSbAC13DecompressionO7Logging0Q0VSgtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:backgroundActivityLogger:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/Proxy.html":{"name":"Proxy","abstract":"

Proxy server configuration","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/Timeout.html":{"name":"Timeout","abstract":"

Timeout configuration.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html":{"name":"RedirectConfiguration","abstract":"

Specifies redirect processing settings.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/ConnectionPool.html":{"name":"ConnectionPool","abstract":"

Connection pool configuration.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/HTTPVersion.html":{"name":"HTTPVersion","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C14eventLoopGroup7NIOCore05EventdE0_pvp":{"name":"eventLoopGroup","abstract":"

Undocumented

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C22eventLoopGroupProvider13configurationA2C05EventdeF0O_AC13ConfigurationVtcfc":{"name":"init(eventLoopGroupProvider:configuration:)","abstract":"

Create an HTTPClient with specified EventLoopGroup provider and configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C22eventLoopGroupProvider13configuration24backgroundActivityLoggerA2C05EventdeF0O_AC13ConfigurationV7Logging0J0Vtcfc":{"name":"init(eventLoopGroupProvider:configuration:backgroundActivityLogger:)","abstract":"

Create an HTTPClient with specified EventLoopGroup provider and configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C12syncShutdownyyKF":{"name":"syncShutdown()","abstract":"

Shuts down the client and EventLoopGroup if it was created by the client.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C8shutdown5queue_y8Dispatch0E5QueueC_ys5Error_pSgctF":{"name":"shutdown(queue:_:)","abstract":"

Shuts down the client and event loop gracefully. This function is clearly an outlier in that it uses a completion","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3get3url8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AG11NIODeadlineVSgtF":{"name":"get(url:deadline:)","abstract":"

Execute GET request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3get3url8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"get(url:deadline:logger:)","abstract":"

Execute GET request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C4post3url4body8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"post(url:body:deadline:)","abstract":"

Execute POST request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C4post3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"post(url:body:deadline:logger:)","abstract":"

Execute POST request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C5patch3url4body8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"patch(url:body:deadline:)","abstract":"

Execute PATCH request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C5patch3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"patch(url:body:deadline:logger:)","abstract":"

Execute PATCH request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3put3url4body8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"put(url:body:deadline:)","abstract":"

Execute PUT request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3put3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"put(url:body:deadline:logger:)","abstract":"

Execute PUT request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C6delete3url8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AG11NIODeadlineVSgtF":{"name":"delete(url:deadline:)","abstract":"

Execute DELETE request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C6delete3url8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"delete(url:deadline:logger:)","abstract":"

Execute DELETE request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_SSAC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:url:body:deadline:logger:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_10socketPath03urlE04body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_S2SAC4BodyVSgAJ11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:socketPath:urlPath:body:deadline:logger:)","abstract":"

Execute arbitrary HTTP+UNIX request to a unix domain socket path, using the specified URL as the request to send to the server.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_16secureSocketPath03urlF04body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_S2SAC4BodyVSgAJ11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:secureSocketPath:urlPath:body:deadline:logger:)","abstract":"

Execute arbitrary HTTPS+UNIX request to a unix domain socket path over TLS, using the specified URL as the request to send to the server.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGAC7RequestV_AG11NIODeadlineVSgtF":{"name":"execute(request:deadline:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGAC7RequestV_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"execute(request:deadline:logger:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request9eventLoop8deadline7NIOCore05EventF6FutureCyAC8ResponseVGAC7RequestV_AC0iF10PreferenceVAH11NIODeadlineVSgtF":{"name":"execute(request:eventLoop:deadline:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request9eventLoop8deadline6logger7NIOCore05EventF6FutureCyAC8ResponseVGAC7RequestV_AC0jF10PreferenceVAI11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(request:eventLoop:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate8deadlineAC4TaskCy_8ResponseQzGAC7RequestV_x7NIOCore11NIODeadlineVSgtAA0bH8DelegateRzlF":{"name":"execute(request:delegate:deadline:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate8deadline6loggerAC4TaskCy_8ResponseQzGAC7RequestV_x7NIOCore11NIODeadlineVSg7Logging6LoggerVtAA0bI8DelegateRzlF":{"name":"execute(request:delegate:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate9eventLoop8deadlineAC4TaskCy_8ResponseQzGAC7RequestV_xAC05EventG10PreferenceV7NIOCore11NIODeadlineVSgtAA0bJ8DelegateRzlF":{"name":"execute(request:delegate:eventLoop:deadline:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate9eventLoop8deadline6loggerAC4TaskCy_8ResponseQzGAC7RequestV_xAC05EventG10PreferenceV7NIOCore11NIODeadlineVSg7Logging6LoggerVSgtAA0bK8DelegateRzlF":{"name":"execute(request:delegate:eventLoop:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Configuration.html":{"name":"Configuration","abstract":"

HTTPClient configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient/EventLoopGroupProvider.html":{"name":"EventLoopGroupProvider","abstract":"

Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

","parent_name":"HTTPClient"},"Classes/HTTPClient/EventLoopPreference.html":{"name":"EventLoopPreference","abstract":"

Specifies how the library will treat event loop passed by the user.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Decompression.html":{"name":"Decompression","abstract":"

Specifies decompression settings.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Cookie.html":{"name":"Cookie","abstract":"

A representation of an HTTP cookie.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Response.html":{"name":"Response","abstract":"

Represent HTTP response.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Body.html":{"name":"Body","abstract":"

Represent request body.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Request.html":{"name":"Request","abstract":"

Represent HTTP request.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Authorization.html":{"name":"Authorization","abstract":"

HTTP authentication

","parent_name":"HTTPClient"},"Classes/HTTPClient/Task.html":{"name":"Task","abstract":"

Response execution context. Will be created by the library and could be used for obtaining","parent_name":"HTTPClient"},"Classes/HTTPClient/NWPOSIXError.html":{"name":"NWPOSIXError","parent_name":"HTTPClient"},"Classes/HTTPClient/NWTLSError.html":{"name":"NWTLSError","parent_name":"HTTPClient"},"Classes/FileDownloadDelegate/Progress.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8ProgressV10totalBytesSiSgvp":{"name":"totalBytes","abstract":"

Undocumented

","parent_name":"Progress"},"Classes/FileDownloadDelegate/Progress.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8ProgressV13receivedBytesSivp":{"name":"receivedBytes","abstract":"

Undocumented

","parent_name":"Progress"},"Classes/FileDownloadDelegate/Progress.html":{"name":"Progress","abstract":"

The response type for this delegate: the total count of bytes as reported by the response","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8Responsea":{"name":"Response","abstract":"

Undocumented

","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient20FileDownloadDelegateC4path4pool10reportHead0H8ProgressACSS_8NIOPosix13NIOThreadPoolCy8NIOHTTP1012HTTPResponseI0VcSgyAC0J0VcSgtKcfc":{"name":"init(path:pool:reportHead:reportProgress:)","abstract":"

Initializes a new file download delegate.

","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html":{"name":"FileDownloadDelegate","abstract":"

Handles a streaming download to a given file path, allowing headers and progress to be reported.

"},"Classes/HTTPClient.html":{"name":"HTTPClient","abstract":"

HTTPClient class provides API for request execution.

"},"Classes/ResponseAccumulator.html":{"name":"ResponseAccumulator","abstract":"

Undocumented

"},"Classes/HTTPClientCopyingDelegate.html":{"name":"HTTPClientCopyingDelegate","abstract":"

Undocumented

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Extensions.html":{"name":"Extensions","abstract":"

The following extensions are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"},"Structs.html":{"name":"Structures","abstract":"

The following structures are available globally.

"}} \ No newline at end of file diff --git a/docs/1.8.1/AsyncHTTPClient/undocumented.json b/docs/1.8.1/AsyncHTTPClient/undocumented.json new file mode 100644 index 000000000..b86904ec5 --- /dev/null +++ b/docs/1.8.1/AsyncHTTPClient/undocumented.json @@ -0,0 +1,201 @@ +{ + "warnings": [ + { + "file": "/code/Sources/AsyncHTTPClient/ConnectionPool/HTTPConnectionPool+Manager.swift", + "line": 128, + "symbol": "HTTPConnectionPool.Manager", + "symbol_kind": "source.lang.swift.decl.extension", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/ConnectionPool/HTTPRequestStateMachine.swift", + "line": 767, + "symbol": "HTTPRequestStateMachine.ResponseState", + "symbol_kind": "source.lang.swift.decl.extension", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP1StateMachine.swift", + "line": 617, + "symbol": "HTTPConnectionPool.HTTP1StateMachine", + "symbol_kind": "source.lang.swift.decl.extension", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+StateMachine.swift", + "line": 303, + "symbol": "HTTPConnectionPool.StateMachine", + "symbol_kind": "source.lang.swift.decl.extension", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 24, + "symbol": "FileDownloadDelegate.Progress.totalBytes", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 25, + "symbol": "FileDownloadDelegate.Progress.receivedBytes", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 30, + "symbol": "FileDownloadDelegate.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 67, + "symbol": "HTTPClient.eventLoopGroup", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 658, + "symbol": "HTTPClient.Configuration.init(tlsConfiguration:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 676, + "symbol": "HTTPClient.Configuration.init(tlsConfiguration:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 693, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:maximumAllowedIdleTimeInConnectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 711, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:backgroundActivityLogger:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 730, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 878, + "symbol": "HTTPClient.Configuration.ConnectionPool.init(idleTimeout:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 882, + "symbol": "HTTPClient.Configuration.ConnectionPool.init(idleTimeout:concurrentHTTP1ConnectionsPerHostSoftLimit:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 888, + "symbol": "HTTPClient.Configuration.HTTPVersion", + "symbol_kind": "source.lang.swift.decl.struct", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 1026, + "symbol": "HTTPClientError.httpEndReceivedAfterHeadWith1xx", + "symbol_kind": "source.lang.swift.decl.var.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 281, + "symbol": "HTTPClient.Authorization.basic(username:password:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 285, + "symbol": "HTTPClient.Authorization.basic(credentials:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 289, + "symbol": "HTTPClient.Authorization.bearer(tokens:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 293, + "symbol": "HTTPClient.Authorization.headerValue", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 304, + "symbol": "ResponseAccumulator", + "symbol_kind": "source.lang.swift.decl.class", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 305, + "symbol": "ResponseAccumulator.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 318, + "symbol": "ResponseAccumulator.init(request:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 411, + "symbol": "HTTPClientResponseDelegate.Response", + "symbol_kind": "source.lang.swift.decl.associatedtype", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 17, + "symbol": "HTTPClientCopyingDelegate", + "symbol_kind": "source.lang.swift.decl.class", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 18, + "symbol": "HTTPClientCopyingDelegate.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 22, + "symbol": "HTTPClientCopyingDelegate.init(chunkHandler:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + } + ], + "source_directory": "/code" +} \ No newline at end of file diff --git a/docs/1.9.0/AsyncHTTPClient/Classes.html b/docs/1.9.0/AsyncHTTPClient/Classes.html new file mode 100644 index 000000000..987a0afdc --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes.html @@ -0,0 +1,311 @@ + + + + Classes Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Classes

+

The following classes are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + FileDownloadDelegate + +
    +
    +
    +
    +
    +
    +

    Handles a streaming download to a given file path, allowing headers and progress to be reported.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class FileDownloadDelegate : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + HTTPClient + +
    +
    +
    +
    +
    +
    +

    HTTPClient class provides API for request execution.

    + +

    Example:

    +
        let client = HTTPClient(eventLoopGroupProvider: .createNew)
    +    client.get(url: "https://swift.org/", deadline: .now() + .seconds(1)).whenComplete { result in
    +        switch result {
    +        case .failure(let error):
    +            // process error
    +        case .success(let response):
    +            if let response.status == .ok {
    +                // handle response
    +            } else {
    +                // handle remote error
    +            }
    +        }
    +    }
    +
    + +

    It is important to close the client instance, for example in a defer statement, after use to cleanly shutdown the underlying NIO EventLoopGroup:

    +
        try client.syncShutdown()
    +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class HTTPClient
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + ResponseAccumulator + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class ResponseAccumulator : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Undocumented

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class HTTPClientCopyingDelegate : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/FileDownloadDelegate.html b/docs/1.9.0/AsyncHTTPClient/Classes/FileDownloadDelegate.html new file mode 100644 index 000000000..7a37c1566 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/FileDownloadDelegate.html @@ -0,0 +1,454 @@ + + + + FileDownloadDelegate Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

FileDownloadDelegate

+
+
+ +
public final class FileDownloadDelegate : HTTPClientResponseDelegate
+ +
+
+

Handles a streaming download to a given file path, allowing headers and progress to be reported.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + Progress + +
    +
    +
    +
    +
    +
    +

    The response type for this delegate: the total count of bytes as reported by the response +“Content-Length” header (if available) and the count of bytes downloaded.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Progress
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public typealias Response = Progress
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Initializes a new file download delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(
    +    path: String,
    +    pool: NIOThreadPool = NIOThreadPool(numberOfThreads: 1),
    +    reportHead: ((HTTPResponseHead) -> Void)? = nil,
    +    reportProgress: ((Progress) -> Void)? = nil
    +) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + path + + +
    +

    Path to a file you’d like to write the download to.

    +
    +
    + + pool + + +
    +

    A thread pool to use for asynchronous file I/O.

    +
    +
    + + reportHead + + +
    +

    A closure called when the response head is available.

    +
    +
    + + reportProgress + + +
    +

    A closure called when a body chunk has been downloaded, with +the total byte count and download byte count passed to it as arguments. The callbacks +will be invoked in the same threading context that the delegate itself is invoked, +as controlled by EventLoopPreference.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveHead(
    +    task: HTTPClient.Task<Response>,
    +    _ head: HTTPResponseHead
    +) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveBodyPart(
    +    task: HTTPClient.Task<Response>,
    +    _ buffer: ByteBuffer
    +) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveError(task: HTTPClient.Task<Progress>, _ error: Error)
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didFinishRequest(task: HTTPClient.Task<Response>) throws -> Response
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html b/docs/1.9.0/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html new file mode 100644 index 000000000..d6e0e7653 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html @@ -0,0 +1,238 @@ + + + + Progress Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Progress

+
+
+ +
public struct Progress
+ +
+
+

The response type for this delegate: the total count of bytes as reported by the response +“Content-Length” header (if available) and the count of bytes downloaded.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + totalBytes + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var totalBytes: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + receivedBytes + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var receivedBytes: Int
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient.html b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient.html new file mode 100644 index 000000000..0f79e41e1 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient.html @@ -0,0 +1,2483 @@ + + + + HTTPClient Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClient

+
+
+ +
public class HTTPClient
+ +
+
+

HTTPClient class provides API for request execution.

+ +

Example:

+
    let client = HTTPClient(eventLoopGroupProvider: .createNew)
+    client.get(url: "https://swift.org/", deadline: .now() + .seconds(1)).whenComplete { result in
+        switch result {
+        case .failure(let error):
+            // process error
+        case .success(let response):
+            if let response.status == .ok {
+                // handle response
+            } else {
+                // handle remote error
+            }
+        }
+    }
+
+ +

It is important to close the client instance, for example in a defer statement, after use to cleanly shutdown the underlying NIO EventLoopGroup:

+
    try client.syncShutdown()
+
+ + +
+
+ +
+
+
+
    +
  • +
    + + + + eventLoopGroup + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let eventLoopGroup: EventLoopGroup
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTPClient with specified EventLoopGroup provider and configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public convenience init(eventLoopGroupProvider: EventLoopGroupProvider,
    +                        configuration: Configuration = Configuration())
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + eventLoopGroupProvider + + +
    +

    Specify how EventLoopGroup will be created.

    +
    +
    + + configuration + + +
    +

    Client configuration.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTPClient with specified EventLoopGroup provider and configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public required init(eventLoopGroupProvider: EventLoopGroupProvider,
    +                     configuration: Configuration = Configuration(),
    +                     backgroundActivityLogger: Logger)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + eventLoopGroupProvider + + +
    +

    Specify how EventLoopGroup will be created.

    +
    +
    + + configuration + + +
    +

    Client configuration.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + syncShutdown() + +
    +
    +
    +
    +
    +
    +

    Shuts down the client and EventLoopGroup if it was created by the client.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func syncShutdown() throws
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + shutdown(queue:_:) + +
    +
    +
    +
    +
    +
    +

    Shuts down the client and event loop gracefully. This function is clearly an outlier in that it uses a completion +callback instead of an EventLoopFuture. The reason for that is that NIO’s EventLoopFutures will call back on an event loop. +The virtue of this function is to shut the event loop down. To work around that we call back on a DispatchQueue +instead.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func shutdown(queue: DispatchQueue = .global(), _ callback: @escaping (Error?) -> Void)
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + get(url:deadline:) + +
    +
    +
    +
    +
    +
    +

    Execute GET request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func get(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute GET request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func get(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute POST request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute POST request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PATCH request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PATCH request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PUT request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PUT request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + delete(url:deadline:) + +
    +
    +
    +
    +
    +
    +

    Execute DELETE request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func delete(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    The time when the request must have been completed by.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute DELETE request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func delete(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    The time when the request must have been completed by.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + url + + +
    +

    Request url.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP+UNIX request to a unix domain socket path, using the specified URL as the request to send to the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, socketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + urlPath + + +
    +

    The URL path and query that will be sent to the server.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTPS+UNIX request to a unix domain socket path over TLS, using the specified URL as the request to send to the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, secureSocketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + secureSocketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + urlPath + + +
    +

    The URL path and query that will be sent to the server.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, eventLoop: EventLoopPreference, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request,
    +                    eventLoop eventLoopPreference: EventLoopPreference,
    +                    deadline: NIODeadline? = nil,
    +                    logger: Logger?) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          deadline: NIODeadline? = nil) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          deadline: NIODeadline? = nil,
    +                                                          logger: Logger) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          eventLoop eventLoopPreference: EventLoopPreference,
    +                                                          deadline: NIODeadline? = nil) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(
    +    request: Request,
    +    delegate: Delegate,
    +    eventLoop eventLoopPreference: EventLoopPreference,
    +    deadline: NIODeadline? = nil,
    +    logger originalLogger: Logger?
    +) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + Configuration + +
    +
    +
    +
    +
    +
    +

    HTTPClient configuration.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Configuration
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum EventLoopGroupProvider
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + EventLoopPreference + +
    +
    +
    +
    +
    +
    +

    Specifies how the library will treat event loop passed by the user.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct EventLoopPreference
    +
    extension HTTPClient.EventLoopPreference: CustomStringConvertible
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Decompression + +
    +
    +
    +
    +
    +
    +

    Specifies decompression settings.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum Decompression
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Cookie + +
    +
    +
    +
    +
    +
    +

    A representation of an HTTP cookie.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Cookie
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Body + +
    +
    +
    +
    +
    +
    +

    Represent request body.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Body
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Represent HTTP response.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Response
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Request + +
    +
    +
    +
    +
    +
    +

    Represent HTTP request.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Request
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Authorization + +
    +
    +
    +
    +
    +
    +

    HTTP authentication

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Authorization : Hashable
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Task + +
    +
    +
    +
    +
    +
    +

    Response execution context. Will be created by the library and could be used for obtaining +EventLoopFuture<Response> of the execution or cancellation of the execution.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class Task<Response>
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + NWPOSIXError + +
    +
    +
    +
    +
    +
    + + See more +
    +
    +
    +
  • +
  • +
    + + + + NWTLSError + +
    +
    +
    +
    +
    +
    + + See more +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Authorization.html b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Authorization.html new file mode 100644 index 000000000..e139a305d --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Authorization.html @@ -0,0 +1,297 @@ + + + + Authorization Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Authorization

+
+
+ +
public struct Authorization : Hashable
+ +
+
+

HTTP authentication

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Body.html b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Body.html new file mode 100644 index 000000000..49e698258 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Body.html @@ -0,0 +1,528 @@ + + + + Body Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Body

+
+
+ +
public struct Body
+ +
+
+

Represent request body.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + StreamWriter + +
    +
    +
    +
    +
    +
    +

    Chunk provider.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct StreamWriter
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + length + +
    +
    +
    +
    +
    +
    +

    Body size. if nil,Transfer-Encoding will automatically be set to chunked. Otherwise a Content-Length +header is set with the given length.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var length: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + stream + +
    +
    +
    +
    +
    +
    +

    Body chunk provider.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var stream: (StreamWriter) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + byteBuffer(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using ByteBuffer.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func byteBuffer(_ buffer: ByteBuffer) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + buffer + + +
    +

    Body ByteBuffer representation.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + stream(length:_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using StreamWriter.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func stream(length: Int? = nil, _ stream: @escaping (StreamWriter) -> EventLoopFuture<Void>) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + length + + +
    +

    Body size. If nil, Transfer-Encoding will automatically be set to chunked. Otherwise a Content-Length +header is set with the given length.

    +
    +
    + + stream + + +
    +

    Body chunk provider.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + bytes(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using a collection of bytes.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    @inlinable
    +public static func bytes<Bytes>(_ bytes: Bytes) -> Body where Bytes : RandomAccessCollection, Bytes.Element == UInt8
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    Body binary representation.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + string(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using String.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func string(_ string: String) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + string + + +
    +

    Body String representation.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + data(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using Data.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func data(_ data: Data) -> HTTPClient.Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + bytes + + +
    +

    Body Data representation.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html new file mode 100644 index 000000000..72fbefe93 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html @@ -0,0 +1,275 @@ + + + + StreamWriter Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

StreamWriter

+
+
+ +
public struct StreamWriter
+ +
+
+

Chunk provider.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + init(closure:) + +
    +
    +
    +
    +
    +
    +

    Create new StreamWriter

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(closure: @escaping (IOData) -> EventLoopFuture<Void>)
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + closure + + +
    +

    function that will be called to write actual bytes to the channel.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + write(_:) + +
    +
    +
    +
    +
    +
    +

    Write data to server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func write(_ data: IOData) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    IOData to write.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Configuration.html b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Configuration.html new file mode 100644 index 000000000..ab234f1ff --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Configuration.html @@ -0,0 +1,775 @@ + + + + Configuration Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Configuration

+
+
+ +
public struct Configuration
+ +
+
+

HTTPClient configuration.

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html new file mode 100644 index 000000000..ff2858d1a --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html @@ -0,0 +1,299 @@ + + + + ConnectionPool Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

ConnectionPool

+
+
+ +
public struct ConnectionPool : Hashable
+ +
+
+

Connection pool configuration.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + idleTimeout + +
    +
    +
    +
    +
    +
    +

    Specifies amount of time connections are kept idle in the pool. After this time has passed without a new +request the connections are closed.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var idleTimeout: TimeAmount
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The maximum number of connections that are kept alive in the connection pool per host. If requests with +an explicit eventLoopRequirement are sent, this number might be exceeded due to overflow connections.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var concurrentHTTP1ConnectionsPerHostSoftLimit: Int
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + init(idleTimeout:) + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(idleTimeout: TimeAmount = .seconds(60))
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(idleTimeout: TimeAmount, concurrentHTTP1ConnectionsPerHostSoftLimit: Int)
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/HTTPVersion.html b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/HTTPVersion.html new file mode 100644 index 000000000..8a80b61ec --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/HTTPVersion.html @@ -0,0 +1,237 @@ + + + + HTTPVersion Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPVersion

+
+
+ +
public struct HTTPVersion
+ +
+
+

Undocumented

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + http1Only + +
    +
    +
    +
    +
    +
    +

    we only use HTTP/1, even if the server would supports HTTP/2

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let http1Only: `Self`
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + automatic + +
    +
    +
    +
    +
    +
    +

    HTTP/2 is used if we connect to a server with HTTPS and the server supports HTTP/2, otherwise we use HTTP/1

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let automatic: `Self`
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html new file mode 100644 index 000000000..7ed7a1f9c --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html @@ -0,0 +1,475 @@ + + + + Proxy Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Proxy

+
+
+ +
public struct Proxy
+ +
+
+

Proxy server configuration +Specifies the remote address of an HTTP proxy.

+ +

Adding an Proxy to your client’s HTTPClient.Configuration +will cause requests to be passed through the specified proxy using the +HTTP CONNECT method.

+ +

If a TLSConfiguration is used in conjunction with HTTPClient.Configuration.Proxy, +TLS will be established after successful proxy, between your client +and the destination server.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server host.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + port + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server port.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var port: Int
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + authorization + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server authorization.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var authorization: HTTPClient.Authorization? { get set }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + server(host:port:) + +
    +
    +
    +
    +
    +
    +

    Create a HTTP proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func server(host: String, port: Int) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + host + + +
    +

    proxy server host.

    +
    +
    + + port + + +
    +

    proxy server port.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a HTTP proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func server(host: String, port: Int, authorization: HTTPClient.Authorization? = nil) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + host + + +
    +

    proxy server host.

    +
    +
    + + port + + +
    +

    proxy server port.

    +
    +
    + + authorization + + +
    +

    proxy server authorization.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a SOCKSv5 proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func socksServer(host: String, port: Int = 1080) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + host + + +
    +

    The SOCKSv5 proxy address.

    +
    +
    + + port + + +
    +

    The SOCKSv5 proxy port, defaults to 1080.

    +
    +
    +
    +
    +

    Return Value

    +

    A new instance of Proxy configured to connect to a SOCKSv5 server.

    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html new file mode 100644 index 000000000..7e30aef4f --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html @@ -0,0 +1,273 @@ + + + + RedirectConfiguration Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

RedirectConfiguration

+
+
+ +
public struct RedirectConfiguration
+ +
+
+

Specifies redirect processing settings.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + disallow + +
    +
    +
    +
    +
    +
    +

    Redirects are not followed.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let disallow: HTTPClient.Configuration.RedirectConfiguration
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Redirects are followed with a specified limit.

    +
    +

    Warning

    +

    Cycle detection will keep all visited URLs in memory which means a malicious server could use this as a denial-of-service vector.

    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func follow(max: Int, allowCycles: Bool) -> RedirectConfiguration
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + max + + +
    +

    The maximum number of allowed redirects.

    +
    +
    + + allowCycles + + +
    +

    Whether cycles are allowed.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html new file mode 100644 index 000000000..c64031505 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html @@ -0,0 +1,299 @@ + + + + Timeout Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Timeout

+
+
+ +
public struct Timeout
+ +
+
+

Timeout configuration.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + connect + +
    +
    +
    +
    +
    +
    +

    Specifies connect timeout. If no connect timeout is given, a default 30 seconds timeout will applied.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var connect: TimeAmount?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + read + +
    +
    +
    +
    +
    +
    +

    Specifies read timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var read: TimeAmount?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + init(connect:read:) + +
    +
    +
    +
    +
    +
    +

    Create timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(connect: TimeAmount? = nil, read: TimeAmount? = nil)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + connect + + +
    +

    connect timeout. Will default to 10 seconds, if no value is +provided. See var connectionCreationTimeout

    +
    +
    + + read + + +
    +

    read timeout.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Cookie.html b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Cookie.html new file mode 100644 index 000000000..e5df1ebb5 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Cookie.html @@ -0,0 +1,615 @@ + + + + Cookie Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Cookie

+
+
+ +
public struct Cookie
+ +
+
+

A representation of an HTTP cookie.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + name + +
    +
    +
    +
    +
    +
    +

    The name of the cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var name: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + value + +
    +
    +
    +
    +
    +
    +

    The cookie’s string value.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var value: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + path + +
    +
    +
    +
    +
    +
    +

    The cookie’s path.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var path: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + domain + +
    +
    +
    +
    +
    +
    +

    The domain of the cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var domain: String?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + maxAge + +
    +
    +
    +
    +
    +
    +

    The cookie’s age in seconds.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var maxAge: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + httpOnly + +
    +
    +
    +
    +
    +
    +

    Whether the cookie should only be sent to HTTP servers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var httpOnly: Bool
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + secure + +
    +
    +
    +
    +
    +
    +

    Whether the cookie should only be sent over secure channels.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var secure: Bool
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a Cookie by parsing a Set-Cookie header.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(header: String, defaultDomain: String)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + header + + +
    +

    String representation of the Set-Cookie response header.

    +
    +
    + + defaultDomain + + +
    +

    Default domain to use if cookie was sent without one.

    +
    +
    +
    +
    +

    Return Value

    +

    nil if the header is invalid.

    +
    + +
    +
    +
  • +
  • +
    + + + + expires + +
    +
    +
    +
    +
    +
    +

    The cookie’s expiration date.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var expires: Date? { get set }
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(name: String, value: String, path: String = "/", domain: String? = nil, expires: Date? = nil, maxAge: Int? = nil, httpOnly: Bool = false, secure: Bool = false)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + name + + +
    +

    The name of the cookie.

    +
    +
    + + value + + +
    +

    The cookie’s string value.

    +
    +
    + + path + + +
    +

    The cookie’s path.

    +
    +
    + + domain + + +
    +

    The domain of the cookie, defaults to nil.

    +
    +
    + + expires + + +
    +

    The cookie’s expiration date, defaults to nil.

    +
    +
    + + maxAge + + +
    +

    The cookie’s age in seconds, defaults to nil.

    +
    +
    + + httpOnly + + +
    +

    Whether this cookie should be used by HTTP servers only, defaults to false.

    +
    +
    + + secure + + +
    +

    Whether this cookie should only be sent using secure channels, defaults to false.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Decompression.html b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Decompression.html new file mode 100644 index 000000000..bda371b90 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Decompression.html @@ -0,0 +1,237 @@ + + + + Decompression Enumeration Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Decompression

+
+
+ +
public enum Decompression
+ +
+
+

Specifies decompression settings.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + disabled + +
    +
    +
    +
    +
    +
    +

    Decompression is disabled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case disabled
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + enabled(limit:) + +
    +
    +
    +
    +
    +
    +

    Decompression is enabled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case enabled(limit: NIOHTTPDecompression.DecompressionLimit)
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html new file mode 100644 index 000000000..b67dd97c2 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html @@ -0,0 +1,237 @@ + + + + EventLoopGroupProvider Enumeration Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

EventLoopGroupProvider

+
+
+ +
public enum EventLoopGroupProvider
+ +
+
+

Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + shared(_:) + +
    +
    +
    +
    +
    +
    +

    EventLoopGroup will be provided by the user. Owner of this group is responsible for its lifecycle.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case shared(EventLoopGroup)
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + createNew + +
    +
    +
    +
    +
    +
    +

    EventLoopGroup will be created by the client. When syncShutdown is called, created EventLoopGroup will be shut down as well.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case createNew
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html new file mode 100644 index 000000000..d0a36a7ac --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html @@ -0,0 +1,304 @@ + + + + EventLoopPreference Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

EventLoopPreference

+
+
+ +
public struct EventLoopPreference
+
extension HTTPClient.EventLoopPreference: CustomStringConvertible
+ +
+
+

Specifies how the library will treat event loop passed by the user.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + indifferent + +
    +
    +
    +
    +
    +
    +

    Event Loop will be selected by the library.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let indifferent: HTTPClient.EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + delegate(on:) + +
    +
    +
    +
    +
    +
    +

    The delegate will be run on the specified EventLoop (and the Channel if possible).

    + +

    This will call the configured delegate on eventLoop and will try to use a Channel on the same +EventLoop but will not establish a new network connection just to satisfy the EventLoop preference if +another existing connection on a different EventLoop is readily available from a connection pool.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func delegate(on eventLoop: EventLoop) -> EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The delegate and the Channel will be run on the specified EventLoop.

    + +

    Use this for use-cases where you prefer a new connection to be established over re-using an existing +connection that might be on a different EventLoop.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func delegateAndChannel(on eventLoop: EventLoop) -> EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var description: String { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/NWPOSIXError.html b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/NWPOSIXError.html new file mode 100644 index 000000000..aa1574485 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/NWPOSIXError.html @@ -0,0 +1,222 @@ + + + + NWPOSIXError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

NWPOSIXError

+ +
+
+ +
+
+
+
    +
  • +
    + + + + errorCode + +
    +
    +
    +
    +
    +
    +

    POSIX error code (enum)

    + +
    +
    +
    +
  • +
  • +
    + + + + init(_:reason:) + +
    +
    +
    +
    +
    +
    +

    Initialise a NWPOSIXError

    + +
    +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/NWTLSError.html b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/NWTLSError.html new file mode 100644 index 000000000..ded7690d5 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/NWTLSError.html @@ -0,0 +1,222 @@ + + + + NWTLSError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

NWTLSError

+ +
+
+ +
+
+
+
    +
  • +
    + + + + status + +
    +
    +
    +
    +
    +
    +

    TLS error status. List of TLS errors can be found in

    + +
    +
    +
    +
  • +
  • +
    + + + + init(_:reason:) + +
    +
    +
    +
    +
    +
    +

    initialise a NWTLSError

    + +
    +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Request.html b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Request.html new file mode 100644 index 000000000..61a6ba202 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Request.html @@ -0,0 +1,875 @@ + + + + Request Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Request

+
+
+ +
public struct Request
+ +
+
+

Represent HTTP request.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + method + +
    +
    +
    +
    +
    +
    +

    Request HTTP method, defaults to GET.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let method: HTTPMethod
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + url + +
    +
    +
    +
    +
    +
    +

    Remote URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let url: URL
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + scheme + +
    +
    +
    +
    +
    +
    +

    Remote HTTP scheme, resolved from URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var scheme: String { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + headers + +
    +
    +
    +
    +
    +
    +

    Request custom HTTP Headers, defaults to no headers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var headers: HTTPHeaders
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + body + +
    +
    +
    +
    +
    +
    +

    Request body, defaults to no body.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var body: Body?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + tlsConfiguration + +
    +
    +
    +
    +
    +
    +

    Request-specific TLS configuration, defaults to no request-specific TLS configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var tlsConfiguration: TLSConfiguration?
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP request.

    +
    +

    Throws

    +
      +
    • invalidURL if URL cannot be parsed.
    • +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: String, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + version + + +
    +

    HTTP version.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP request.

    +
    +

    Throws

    +
      +
    • invalidURL if URL cannot be parsed.
    • +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: String, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil, tlsConfiguration: TLSConfiguration?) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + version + + +
    +

    HTTP version.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + tlsConfiguration + + +
    +

    Request TLS configuration

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTP Request.

    +
    +

    Throws

    +
      +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    • missingSocketPath if URL does not contains a socketPath as an encoded host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTP Request.

    +
    +

    Throws

    +
      +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    • missingSocketPath if URL does not contains a socketPath as an encoded host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil, tlsConfiguration: TLSConfiguration?) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + tlsConfiguration + + +
    +

    Request TLS configuration

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Remote host, resolved from URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + port + +
    +
    +
    +
    +
    +
    +

    Resolved port.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var port: Int { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + useTLS + +
    +
    +
    +
    +
    +
    +

    Whether request will be executed using secure socket.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var useTLS: Bool { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Response.html b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Response.html new file mode 100644 index 000000000..794565158 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Response.html @@ -0,0 +1,540 @@ + + + + Response Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Response

+
+
+ +
public struct Response
+ +
+
+

Represent HTTP response.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Remote host of the request.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + status + +
    +
    +
    +
    +
    +
    +

    Response HTTP status.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var status: HTTPResponseStatus
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + version + +
    +
    +
    +
    +
    +
    +

    Response HTTP version.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var version: HTTPVersion
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + headers + +
    +
    +
    +
    +
    +
    +

    Reponse HTTP headers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var headers: HTTPHeaders
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + body + +
    +
    +
    +
    +
    +
    +

    Response body.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var body: ByteBuffer?
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP Response.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    @available(*, deprecated, renamed: "init(host:status:version:headers:body:﹚")
    +public init(host: String, status: HTTPResponseStatus, headers: HTTPHeaders, body: ByteBuffer?)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + host + + +
    +

    Remote host of the request.

    +
    +
    + + status + + +
    +

    Response HTTP status.

    +
    +
    + + headers + + +
    +

    Reponse HTTP headers.

    +
    +
    + + body + + +
    +

    Response body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP Response.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(host: String, status: HTTPResponseStatus, version: HTTPVersion, headers: HTTPHeaders, body: ByteBuffer?)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + host + + +
    +

    Remote host of the request.

    +
    +
    + + status + + +
    +

    Response HTTP status.

    +
    +
    + + version + + +
    +

    Response HTTP version.

    +
    +
    + + headers + + +
    +

    Reponse HTTP headers.

    +
    +
    + + body + + +
    +

    Response body.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + cookies + +
    +
    +
    +
    +
    +
    +

    List of HTTP cookies returned by the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var cookies: [HTTPClient.Cookie] { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Task.html b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Task.html new file mode 100644 index 000000000..47ca25e2b --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClient/Task.html @@ -0,0 +1,307 @@ + + + + Task Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Task

+
+
+ +
public final class Task<Response>
+ +
+
+

Response execution context. Will be created by the library and could be used for obtaining +EventLoopFuture<Response> of the execution or cancellation of the execution.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + eventLoop + +
    +
    +
    +
    +
    +
    +

    The EventLoop the delegate will be executed on.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let eventLoop: EventLoop
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + futureResult + +
    +
    +
    +
    +
    +
    +

    EventLoopFuture for the response returned by this request.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var futureResult: EventLoopFuture<Response> { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + wait() + +
    +
    +
    +
    +
    +
    +

    Waits for execution of this request to complete.

    +
    +

    Throws

    + The error value of the EventLoopFuture if it errors. + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func wait() throws -> Response
    + +
    +
    +
    +

    Return Value

    +

    The value of the EventLoopFuture when it completes.

    +
    + +
    +
    +
  • +
  • +
    + + + + cancel() + +
    +
    +
    +
    +
    +
    +

    Cancels the request execution.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func cancel()
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html new file mode 100644 index 000000000..9d0300b75 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html @@ -0,0 +1,295 @@ + + + + HTTPClientCopyingDelegate Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientCopyingDelegate

+
+
+ +
public final class HTTPClientCopyingDelegate : HTTPClientResponseDelegate
+ +
+
+

Undocumented

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Classes/ResponseAccumulator.html b/docs/1.9.0/AsyncHTTPClient/Classes/ResponseAccumulator.html new file mode 100644 index 000000000..63cb719cb --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Classes/ResponseAccumulator.html @@ -0,0 +1,353 @@ + + + + ResponseAccumulator Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

ResponseAccumulator

+
+
+ +
public class ResponseAccumulator : HTTPClientResponseDelegate
+ +
+
+

Undocumented

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Extensions.html b/docs/1.9.0/AsyncHTTPClient/Extensions.html new file mode 100644 index 000000000..85c3344f6 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Extensions.html @@ -0,0 +1,194 @@ + + + + Extensions Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Extensions

+

The following extensions are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + URL + +
    +
    +
    +
    +
    +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    extension URL
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Extensions/URL.html b/docs/1.9.0/AsyncHTTPClient/Extensions/URL.html new file mode 100644 index 000000000..a079e77c4 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Extensions/URL.html @@ -0,0 +1,295 @@ + + + + URL Extension Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

URL

+
+
+ +
extension URL
+ +
+
+ +
+
+ +
+
+
+
    +
  • + +
    +
    +
    +
    +
    +

    Initializes a newly created HTTP URL connecting to a unix domain socket path. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “http+unix” scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(httpURLWithSocketPath socketPath: String, uri: String = "/")
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + uri + + +
    +

    The URI path and query that will be sent to the server.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Initializes a newly created HTTPS URL connecting to a unix domain socket path over TLS. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “https+unix” scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(httpsURLWithSocketPath socketPath: String, uri: String = "/")
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + uri + + +
    +

    The URI path and query that will be sent to the server.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Protocols.html b/docs/1.9.0/AsyncHTTPClient/Protocols.html new file mode 100644 index 000000000..9b8f97726 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Protocols.html @@ -0,0 +1,230 @@ + + + + Protocols Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Protocols

+

The following protocols are available globally.

+ +
+
+ +
+
+
+
    +
  • + +
    +
    +
    +
    +
    +

    HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed. +You can implement this protocol if you need fine-grained control over an HTTP request/response, for example, if you want to inspect the response +headers before deciding whether to accept a response body, or if you want to stream your request body. Pass an instance of your conforming +class to the HTTPClient.execute() method and this package will call each delegate method appropriately as the request takes place./

    +

    Backpressure

    + +

    A HTTPClientResponseDelegate can be used to exert backpressure on the server response. This is achieved by way of the futures returned from +didReceiveHead and didReceiveBodyPart. The following functions are part of the “backpressure system” in the delegate:

    + +
      +
    • didReceiveHead
    • +
    • didReceiveBodyPart
    • +
    • didFinishRequest
    • +
    • didReceiveError
    • +
    + +

    The first three methods are strictly exclusive, with that exclusivity managed by the futures returned by didReceiveHead and +didReceiveBodyPart. What this means is that until the returned future is completed, none of these three methods will be called +again. This allows delegates to rate limit the server to a capacity it can manage. didFinishRequest does not return a future, +as we are expecting no more data from the server at this time.

    + +

    didReceiveError is somewhat special: it signals the end of this regime. didRecieveError is not exclusive: it may be called at +any time, even if a returned future is not yet completed. didReceiveError is terminal, meaning that once it has been called none +of these four methods will be called again. This can be used as a signal to abandon all outstanding work.

    +
    +

    Note

    + This delegate is strongly held by the HTTPTaskHandler + for the duration of the Request processing and will be + released together with the HTTPTaskHandler when channel is closed. + Users of the library are not required to keep a reference to the + object that implements this protocol, but may do so if needed. + +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public protocol HTTPClientResponseDelegate : AnyObject
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html b/docs/1.9.0/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html new file mode 100644 index 000000000..a819ae164 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html @@ -0,0 +1,712 @@ + + + + HTTPClientResponseDelegate Protocol Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientResponseDelegate

+
+
+ +
public protocol HTTPClientResponseDelegate : AnyObject
+ +
+
+

HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed. +You can implement this protocol if you need fine-grained control over an HTTP request/response, for example, if you want to inspect the response +headers before deciding whether to accept a response body, or if you want to stream your request body. Pass an instance of your conforming +class to the HTTPClient.execute() method and this package will call each delegate method appropriately as the request takes place./

+

Backpressure

+ +

A HTTPClientResponseDelegate can be used to exert backpressure on the server response. This is achieved by way of the futures returned from +didReceiveHead and didReceiveBodyPart. The following functions are part of the “backpressure system” in the delegate:

+ +
    +
  • didReceiveHead
  • +
  • didReceiveBodyPart
  • +
  • didFinishRequest
  • +
  • didReceiveError
  • +
+ +

The first three methods are strictly exclusive, with that exclusivity managed by the futures returned by didReceiveHead and +didReceiveBodyPart. What this means is that until the returned future is completed, none of these three methods will be called +again. This allows delegates to rate limit the server to a capacity it can manage. didFinishRequest does not return a future, +as we are expecting no more data from the server at this time.

+ +

didReceiveError is somewhat special: it signals the end of this regime. didRecieveError is not exclusive: it may be called at +any time, even if a returned future is not yet completed. didReceiveError is terminal, meaning that once it has been called none +of these four methods will be called again. This can be used as a signal to abandon all outstanding work.

+
+

Note

+ This delegate is strongly held by the HTTPTaskHandler + for the duration of the Request processing and will be + released together with the HTTPTaskHandler when channel is closed. + Users of the library are not required to keep a reference to the + object that implements this protocol, but may do so if needed. + +
+ + +
+
+ +
+
+
+
    +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    associatedtype Response
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequestHead(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when the request head is sent. Will be called once.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequestHead(task: HTTPClient.Task<Response>, _ head: HTTPRequestHead)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + head + + +
    +

    Request head.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequestPart(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when a part of the request body is sent. Could be called zero or more times.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequestPart(task: HTTPClient.Task<Response>, _ part: IOData)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + part + + +
    +

    Request body Part.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequest(task:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when the request is fully sent. Will be called once.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequest(task: HTTPClient.Task<Response>)
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveHead(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when response head is received. Will be called once. +You must return an EventLoopFuture<Void> that you complete when you have finished processing the body part. +You can create an already succeeded future by calling task.eventLoop.makeSucceededFuture(()).

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveHead(task: HTTPClient.Task<Response>, _ head: HTTPResponseHead) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + head + + +
    +

    Received reposonse head.

    +
    +
    +
    +
    +

    Return Value

    +

    EventLoopFuture that will be used for backpressure.

    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveBodyPart(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when part of a response body is received. Could be called zero or more times. +You must return an EventLoopFuture<Void> that you complete when you have finished processing the body part. +You can create an already succeeded future by calling task.eventLoop.makeSucceededFuture(()).

    + +

    This function will not be called until the future returned by didReceiveHead has completed.

    + +

    This function will not be called for subsequent body parts until the previous future returned by a +call to this function completes.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveBodyPart(task: HTTPClient.Task<Response>, _ buffer: ByteBuffer) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + buffer + + +
    +

    Received body Part.

    +
    +
    +
    +
    +

    Return Value

    +

    EventLoopFuture that will be used for backpressure.

    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveError(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when error was thrown during request execution. Will be called zero or one time only. Request processing will be stopped after that.

    + +

    This function may be called at any time: it does not respect the backpressure exerted by didReceiveHead and didReceiveBodyPart. +All outstanding work may be cancelled when this is received. Once called, no further calls will be made to didReceiveHead, didReceiveBodyPart, +or didFinishRequest.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveError(task: HTTPClient.Task<Response>, _ error: Error)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + error + + +
    +

    Error that occured during response processing.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Called when the complete HTTP request is finished. You must return an instance of your Response associated type. Will be called once, except if an error occurred.

    + +

    This function will not be called until all futures returned by didReceiveHead and didReceiveBodyPart have completed. Once called, +no further calls will be made to didReceiveHead, didReceiveBodyPart, or didReceiveError.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didFinishRequest(task: HTTPClient.Task<Response>) throws -> Response
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    +
    +
    +

    Return Value

    +

    Result of processing.

    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Structs.html b/docs/1.9.0/AsyncHTTPClient/Structs.html new file mode 100644 index 000000000..b44a50d2e --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Structs.html @@ -0,0 +1,198 @@ + + + + Structures Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Structures

+

The following structures are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + HTTPClientError + +
    +
    +
    +
    +
    +
    +

    Possible client errors.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct HTTPClientError : Error, Equatable, CustomStringConvertible
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/Structs/HTTPClientError.html b/docs/1.9.0/AsyncHTTPClient/Structs/HTTPClientError.html new file mode 100644 index 000000000..7d04bffdc --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/Structs/HTTPClientError.html @@ -0,0 +1,1146 @@ + + + + HTTPClientError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientError

+
+
+ +
public struct HTTPClientError : Error, Equatable, CustomStringConvertible
+ +
+
+

Possible client errors.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var description: String { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + invalidURL + +
    +
    +
    +
    +
    +
    +

    URL provided is invalid.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let invalidURL: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + emptyHost + +
    +
    +
    +
    +
    +
    +

    URL does not contain host.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let emptyHost: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + missingSocketPath + +
    +
    +
    +
    +
    +
    +

    URL does not contain a socketPath as a host for http(s)+unix shemes.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let missingSocketPath: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + alreadyShutdown + +
    +
    +
    +
    +
    +
    +

    Client is shutdown and cannot be used for new requests.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let alreadyShutdown: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + emptyScheme + +
    +
    +
    +
    +
    +
    +

    URL does not contain scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let emptyScheme: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + unsupportedScheme(_:) + +
    +
    +
    +
    +
    +
    +

    Provided URL scheme is not supported, supported schemes are: http and https

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func unsupportedScheme(_ scheme: String) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + readTimeout + +
    +
    +
    +
    +
    +
    +

    Request timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let readTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Remote connection was closed unexpectedly.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let remoteConnectionClosed: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + cancelled + +
    +
    +
    +
    +
    +
    +

    Request was cancelled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let cancelled: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Request contains invalid identity encoding.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let identityCodingIncorrectlyPresent: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Request contains multiple chunks definitions.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    @available(*, deprecated, message: "AsyncHTTPClient now silently corrects this invalid header.")
    +public static let chunkedSpecifiedMultipleTimes: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + invalidProxyResponse + +
    +
    +
    +
    +
    +
    +

    Proxy response was invalid.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let invalidProxyResponse: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + contentLengthMissing + +
    +
    +
    +
    +
    +
    +

    Request does not contain Content-Length header.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let contentLengthMissing: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Proxy Authentication Required.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let proxyAuthenticationRequired: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + redirectLimitReached + +
    +
    +
    +
    +
    +
    +

    Redirect Limit reached.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let redirectLimitReached: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + redirectCycleDetected + +
    +
    +
    +
    +
    +
    +

    Redirect Cycle detected.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let redirectCycleDetected: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + uncleanShutdown + +
    +
    +
    +
    +
    +
    +

    Unclean shutdown.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let uncleanShutdown: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + traceRequestWithBody + +
    +
    +
    +
    +
    +
    +

    A body was sent in a request with method TRACE.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let traceRequestWithBody: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Header field names contain invalid characters.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func invalidHeaderFieldNames(_ names: [String]) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + bodyLengthMismatch + +
    +
    +
    +
    +
    +
    +

    Body length is not equal to Content-Length.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let bodyLengthMismatch: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + writeAfterRequestSent + +
    +
    +
    +
    +
    +
    +

    Body part was written after request was fully sent.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let writeAfterRequestSent: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + incompatibleHeaders + +
    +
    +
    +
    +
    +
    +

    Incompatible headers specified, for example Transfer-Encoding and Content-Length.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    @available(*, deprecated, message: "AsyncHTTPClient now silently corrects invalid headers.")
    +public static let incompatibleHeaders: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + connectTimeout + +
    +
    +
    +
    +
    +
    +

    Creating a new tcp connection timed out

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let connectTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + socksHandshakeTimeout + +
    +
    +
    +
    +
    +
    +

    The socks handshake timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let socksHandshakeTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The http proxy connection creation timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let httpProxyHandshakeTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + tlsHandshakeTimeout + +
    +
    +
    +
    +
    +
    +

    The tls handshake timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let tlsHandshakeTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The remote server only offered an unsupported application protocol

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func serverOfferedUnsupportedApplicationProtocol(_ proto: String) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + deadlineExceeded + +
    +
    +
    +
    +
    +
    +

    The request deadline was exceeded. The request was cancelled because of this.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let deadlineExceeded: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The remote server responded with a status code >= 300, before the full request was sent. The request stream +was therefore cancelled

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let requestStreamCancelled: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Aquiring a HTTP connection from the connection pool timed out.

    + +

    This can have multiple reasons:

    + +
      +
    • A connection could not be created within the timout period.
    • +
    • Tasks are not processed fast enough on the existing connections, to process all waiters in time
    • +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let getConnectionFromPoolTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let httpEndReceivedAfterHeadWith1xx: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/badge.svg b/docs/1.9.0/AsyncHTTPClient/badge.svg new file mode 100644 index 000000000..883955dba --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/badge.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + documentation + + + documentation + + + 87% + + + 87% + + + diff --git a/docs/1.9.0/AsyncHTTPClient/css/highlight.css b/docs/1.9.0/AsyncHTTPClient/css/highlight.css new file mode 100644 index 000000000..c170357ce --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/css/highlight.css @@ -0,0 +1,202 @@ +/*! Jazzy - https://github.com/realm/jazzy + * Copyright Realm Inc. + * SPDX-License-Identifier: MIT + */ +/* Credit to https://gist.github.com/wataru420/2048287 */ +.highlight .c { + color: #999988; + font-style: italic; } + +.highlight .err { + color: #a61717; + background-color: #e3d2d2; } + +.highlight .k { + color: #000000; + font-weight: bold; } + +.highlight .o { + color: #000000; + font-weight: bold; } + +.highlight .cm { + color: #999988; + font-style: italic; } + +.highlight .cp { + color: #999999; + font-weight: bold; } + +.highlight .c1 { + color: #999988; + font-style: italic; } + +.highlight .cs { + color: #999999; + font-weight: bold; + font-style: italic; } + +.highlight .gd { + color: #000000; + background-color: #ffdddd; } + +.highlight .gd .x { + color: #000000; + background-color: #ffaaaa; } + +.highlight .ge { + color: #000000; + font-style: italic; } + +.highlight .gr { + color: #aa0000; } + +.highlight .gh { + color: #999999; } + +.highlight .gi { + color: #000000; + background-color: #ddffdd; } + +.highlight .gi .x { + color: #000000; + background-color: #aaffaa; } + +.highlight .go { + color: #888888; } + +.highlight .gp { + color: #555555; } + +.highlight .gs { + font-weight: bold; } + +.highlight .gu { + color: #aaaaaa; } + +.highlight .gt { + color: #aa0000; } + +.highlight .kc { + color: #000000; + font-weight: bold; } + +.highlight .kd { + color: #000000; + font-weight: bold; } + +.highlight .kp { + color: #000000; + font-weight: bold; } + +.highlight .kr { + color: #000000; + font-weight: bold; } + +.highlight .kt { + color: #445588; } + +.highlight .m { + color: #009999; } + +.highlight .s { + color: #d14; } + +.highlight .na { + color: #008080; } + +.highlight .nb { + color: #0086B3; } + +.highlight .nc { + color: #445588; + font-weight: bold; } + +.highlight .no { + color: #008080; } + +.highlight .ni { + color: #800080; } + +.highlight .ne { + color: #990000; + font-weight: bold; } + +.highlight .nf { + color: #990000; } + +.highlight .nn { + color: #555555; } + +.highlight .nt { + color: #000080; } + +.highlight .nv { + color: #008080; } + +.highlight .ow { + color: #000000; + font-weight: bold; } + +.highlight .w { + color: #bbbbbb; } + +.highlight .mf { + color: #009999; } + +.highlight .mh { + color: #009999; } + +.highlight .mi { + color: #009999; } + +.highlight .mo { + color: #009999; } + +.highlight .sb { + color: #d14; } + +.highlight .sc { + color: #d14; } + +.highlight .sd { + color: #d14; } + +.highlight .s2 { + color: #d14; } + +.highlight .se { + color: #d14; } + +.highlight .sh { + color: #d14; } + +.highlight .si { + color: #d14; } + +.highlight .sx { + color: #d14; } + +.highlight .sr { + color: #009926; } + +.highlight .s1 { + color: #d14; } + +.highlight .ss { + color: #990073; } + +.highlight .bp { + color: #999999; } + +.highlight .vc { + color: #008080; } + +.highlight .vg { + color: #008080; } + +.highlight .vi { + color: #008080; } + +.highlight .il { + color: #009999; } diff --git a/docs/1.9.0/AsyncHTTPClient/css/jazzy.css b/docs/1.9.0/AsyncHTTPClient/css/jazzy.css new file mode 100644 index 000000000..c7bb9fe22 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/css/jazzy.css @@ -0,0 +1,404 @@ +/*! Jazzy - https://github.com/realm/jazzy + * Copyright Realm Inc. + * SPDX-License-Identifier: MIT + */ +*, *:before, *:after { + box-sizing: inherit; } + +body { + margin: 0; + background: #fff; + color: #333; + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + letter-spacing: .2px; + -webkit-font-smoothing: antialiased; + box-sizing: border-box; } + +h1 { + font-size: 2rem; + font-weight: 700; + margin: 1.275em 0 0.6em; } + +h2 { + font-size: 1.75rem; + font-weight: 700; + margin: 1.275em 0 0.3em; } + +h3 { + font-size: 1.5rem; + font-weight: 700; + margin: 1em 0 0.3em; } + +h4 { + font-size: 1.25rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h5 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h6 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; + color: #777; } + +p { + margin: 0 0 1em; } + +ul, ol { + padding: 0 0 0 2em; + margin: 0 0 0.85em; } + +blockquote { + margin: 0 0 0.85em; + padding: 0 15px; + color: #858585; + border-left: 4px solid #e5e5e5; } + +img { + max-width: 100%; } + +a { + color: #4183c4; + text-decoration: none; } + a:hover, a:focus { + outline: 0; + text-decoration: underline; } + a.discouraged { + text-decoration: line-through; } + a.discouraged:hover, a.discouraged:focus { + text-decoration: underline line-through; } + +table { + background: #fff; + width: 100%; + border-collapse: collapse; + border-spacing: 0; + overflow: auto; + margin: 0 0 0.85em; } + +tr:nth-child(2n) { + background-color: #fbfbfb; } + +th, td { + padding: 6px 13px; + border: 1px solid #ddd; } + +hr { + height: 1px; + border: none; + background-color: #ddd; } + +pre { + margin: 0 0 1.275em; + padding: .85em 1em; + overflow: auto; + background: #f7f7f7; + font-size: .85em; + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +code { + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +.item-container p > code, .item-container li > code, .top-matter p > code, .top-matter li > code { + background: #f7f7f7; + padding: .2em; } + .item-container p > code:before, .item-container p > code:after, .item-container li > code:before, .item-container li > code:after, .top-matter p > code:before, .top-matter p > code:after, .top-matter li > code:before, .top-matter li > code:after { + letter-spacing: -.2em; + content: "\00a0"; } + +pre code { + padding: 0; + white-space: pre; } + +.content-wrapper { + display: flex; + flex-direction: column; } + @media (min-width: 768px) { + .content-wrapper { + flex-direction: row; } } +.header { + display: flex; + padding: 8px; + font-size: 0.875em; + background: #444; + color: #999; } + +.header-col { + margin: 0; + padding: 0 8px; } + +.header-col--primary { + flex: 1; } + +.header-link { + color: #fff; } + +.header-icon { + padding-right: 2px; + vertical-align: -3px; + height: 16px; } + +.breadcrumbs { + font-size: 0.875em; + padding: 8px 16px; + margin: 0; + background: #fbfbfb; + border-bottom: 1px solid #ddd; } + +.carat { + height: 10px; + margin: 0 5px; } + +.navigation { + order: 2; } + @media (min-width: 768px) { + .navigation { + order: 1; + width: 25%; + max-width: 300px; + padding-bottom: 64px; + overflow: hidden; + word-wrap: normal; + background: #fbfbfb; + border-right: 1px solid #ddd; } } +.nav-groups { + list-style-type: none; + padding-left: 0; } + +.nav-group-name { + border-bottom: 1px solid #ddd; + padding: 8px 0 8px 16px; } + +.nav-group-name-link { + color: #333; } + +.nav-group-tasks { + margin: 8px 0; + padding: 0 0 0 8px; } + +.nav-group-task { + font-size: 1em; + list-style-type: none; + white-space: nowrap; } + +.nav-group-task-link { + color: #808080; } + +.main-content { + order: 1; } + @media (min-width: 768px) { + .main-content { + order: 2; + flex: 1; + padding-bottom: 60px; } } +.section { + padding: 0 32px; + border-bottom: 1px solid #ddd; } + +.section-content { + max-width: 834px; + margin: 0 auto; + padding: 16px 0; } + +.section-name { + color: #666; + display: block; } + .section-name p { + margin-bottom: inherit; } + +.declaration .highlight { + overflow-x: initial; + padding: 8px 0; + margin: 0; + background-color: transparent; + border: none; } + +.task-group-section { + border-top: 1px solid #ddd; } + +.task-group { + padding-top: 0px; } + +.task-name-container a[name]:before { + content: ""; + display: block; } + +.section-name-container { + position: relative; } + .section-name-container .section-name-link { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + margin-bottom: 0; } + .section-name-container .section-name { + position: relative; + pointer-events: none; + z-index: 1; } + .section-name-container .section-name a { + pointer-events: auto; } + +.item-container { + padding: 0; } + +.item { + padding-top: 8px; + width: 100%; + list-style-type: none; } + .item a[name]:before { + content: ""; + display: block; } + .item .token, .item .direct-link { + display: inline-block; + text-indent: -20px; + padding-left: 3px; + margin-left: 20px; + font-size: 1rem; } + .item .declaration-note { + font-size: .85em; + color: #808080; + font-style: italic; } + +.pointer-container { + border-bottom: 1px solid #ddd; + left: -23px; + padding-bottom: 13px; + position: relative; + width: 110%; } + +.pointer { + left: 21px; + top: 7px; + display: block; + position: absolute; + width: 12px; + height: 12px; + border-left: 1px solid #ddd; + border-top: 1px solid #ddd; + background: #fff; + transform: rotate(45deg); } + +.height-container { + display: none; + position: relative; + width: 100%; + overflow: hidden; } + .height-container .section { + background: #fff; + border: 1px solid #ddd; + border-top-width: 0; + padding-top: 10px; + padding-bottom: 5px; + padding: 8px 16px; } + +.aside, .language { + padding: 6px 12px; + margin: 12px 0; + border-left: 5px solid #dddddd; + overflow-y: hidden; } + .aside .aside-title, .language .aside-title { + font-size: 9px; + letter-spacing: 2px; + text-transform: uppercase; + padding-bottom: 0; + margin: 0; + color: #aaa; + -webkit-user-select: none; } + .aside p:last-child, .language p:last-child { + margin-bottom: 0; } + +.language { + border-left: 5px solid #cde9f4; } + .language .aside-title { + color: #4183c4; } + +.aside-warning, .aside-deprecated, .aside-unavailable { + border-left: 5px solid #ff6666; } + .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { + color: #ff0000; } + +.graybox { + border-collapse: collapse; + width: 100%; } + .graybox p { + margin: 0; + word-break: break-word; + min-width: 50px; } + .graybox td { + border: 1px solid #ddd; + padding: 5px 25px 5px 10px; + vertical-align: middle; } + .graybox tr td:first-of-type { + text-align: right; + padding: 7px; + vertical-align: top; + word-break: normal; + width: 40px; } + +.slightly-smaller { + font-size: 0.9em; } + +.footer { + padding: 8px 16px; + background: #444; + color: #ddd; + font-size: 0.8em; } + .footer p { + margin: 8px 0; } + .footer a { + color: #fff; } + +html.dash .header, html.dash .breadcrumbs, html.dash .navigation { + display: none; } + +html.dash .height-container { + display: block; } + +form[role=search] input { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 24px; + padding: 0 10px; + margin: 0; + border: none; + border-radius: 1em; } + .loading form[role=search] input { + background: white url(http://github.com/img/spinner.gif) center right 4px no-repeat; } + +form[role=search] .tt-menu { + margin: 0; + min-width: 300px; + background: #fbfbfb; + color: #333; + border: 1px solid #ddd; } + +form[role=search] .tt-highlight { + font-weight: bold; } + +form[role=search] .tt-suggestion { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 0 8px; } + form[role=search] .tt-suggestion span { + display: table-cell; + white-space: nowrap; } + form[role=search] .tt-suggestion .doc-parent-name { + width: 100%; + text-align: right; + font-weight: normal; + font-size: 0.9em; + padding-left: 16px; } + +form[role=search] .tt-suggestion:hover, +form[role=search] .tt-suggestion.tt-cursor { + cursor: pointer; + background-color: #4183c4; + color: #fff; } + +form[role=search] .tt-suggestion:hover .doc-parent-name, +form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { + color: #fff; } diff --git a/docs/1.9.0/AsyncHTTPClient/img/carat.png b/docs/1.9.0/AsyncHTTPClient/img/carat.png new file mode 100755 index 000000000..29d2f7fd4 Binary files /dev/null and b/docs/1.9.0/AsyncHTTPClient/img/carat.png differ diff --git a/docs/1.9.0/AsyncHTTPClient/img/dash.png b/docs/1.9.0/AsyncHTTPClient/img/dash.png new file mode 100755 index 000000000..6f694c7a0 Binary files /dev/null and b/docs/1.9.0/AsyncHTTPClient/img/dash.png differ diff --git a/docs/1.9.0/AsyncHTTPClient/img/gh.png b/docs/1.9.0/AsyncHTTPClient/img/gh.png new file mode 100755 index 000000000..628da97c7 Binary files /dev/null and b/docs/1.9.0/AsyncHTTPClient/img/gh.png differ diff --git a/docs/1.9.0/AsyncHTTPClient/img/spinner.gif b/docs/1.9.0/AsyncHTTPClient/img/spinner.gif new file mode 100644 index 000000000..e3038d0a4 Binary files /dev/null and b/docs/1.9.0/AsyncHTTPClient/img/spinner.gif differ diff --git a/docs/1.9.0/AsyncHTTPClient/index.html b/docs/1.9.0/AsyncHTTPClient/index.html new file mode 100644 index 000000000..06c894403 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/index.html @@ -0,0 +1,164 @@ + + + + AsyncHTTPClient Reference + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.0 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+ +

AsyncHTTPClient Docs

+ +

AsyncHTTPClient is a Swift HTTP Client package.

+ +

To get started with AsyncHTTPClient, import AsyncHTTPClient. The +most important type is HTTPClient +which you can use to emit log messages.

+ +
+
+ + +
+
+ + + diff --git a/docs/1.9.0/AsyncHTTPClient/js/jazzy.js b/docs/1.9.0/AsyncHTTPClient/js/jazzy.js new file mode 100755 index 000000000..198441660 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/js/jazzy.js @@ -0,0 +1,74 @@ +// Jazzy - https://github.com/realm/jazzy +// Copyright Realm Inc. +// SPDX-License-Identifier: MIT + +window.jazzy = {'docset': false} +if (typeof window.dash != 'undefined') { + document.documentElement.className += ' dash' + window.jazzy.docset = true +} +if (navigator.userAgent.match(/xcode/i)) { + document.documentElement.className += ' xcode' + window.jazzy.docset = true +} + +function toggleItem($link, $content) { + var animationDuration = 300; + $link.toggleClass('token-open'); + $content.slideToggle(animationDuration); +} + +function itemLinkToContent($link) { + return $link.parent().parent().next(); +} + +// On doc load + hash-change, open any targetted item +function openCurrentItemIfClosed() { + if (window.jazzy.docset) { + return; + } + var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); + $content = itemLinkToContent($link); + if ($content.is(':hidden')) { + toggleItem($link, $content); + } +} + +$(openCurrentItemIfClosed); +$(window).on('hashchange', openCurrentItemIfClosed); + +// On item link ('token') click, toggle its discussion +$('.token').on('click', function(event) { + if (window.jazzy.docset) { + return; + } + var $link = $(this); + toggleItem($link, itemLinkToContent($link)); + + // Keeps the document from jumping to the hash. + var href = $link.attr('href'); + if (history.pushState) { + history.pushState({}, '', href); + } else { + location.hash = href; + } + event.preventDefault(); +}); + +// Clicks on links to the current, closed, item need to open the item +$("a:not('.token')").on('click', function() { + if (location == this.href) { + openCurrentItemIfClosed(); + } +}); + +// KaTeX rendering +if ("katex" in window) { + $($('.math').each( (_, element) => { + katex.render(element.textContent, element, { + displayMode: $(element).hasClass('m-block'), + throwOnError: false, + trust: true + }); + })) +} diff --git a/docs/1.9.0/AsyncHTTPClient/js/jazzy.search.js b/docs/1.9.0/AsyncHTTPClient/js/jazzy.search.js new file mode 100644 index 000000000..359cdbb8b --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/js/jazzy.search.js @@ -0,0 +1,74 @@ +// Jazzy - https://github.com/realm/jazzy +// Copyright Realm Inc. +// SPDX-License-Identifier: MIT + +$(function(){ + var $typeahead = $('[data-typeahead]'); + var $form = $typeahead.parents('form'); + var searchURL = $form.attr('action'); + + function displayTemplate(result) { + return result.name; + } + + function suggestionTemplate(result) { + var t = '
'; + t += '' + result.name + ''; + if (result.parent_name) { + t += '' + result.parent_name + ''; + } + t += '
'; + return t; + } + + $typeahead.one('focus', function() { + $form.addClass('loading'); + + $.getJSON(searchURL).then(function(searchData) { + const searchIndex = lunr(function() { + this.ref('url'); + this.field('name'); + this.field('abstract'); + for (const [url, doc] of Object.entries(searchData)) { + this.add({url: url, name: doc.name, abstract: doc.abstract}); + } + }); + + $typeahead.typeahead( + { + highlight: true, + minLength: 3, + autoselect: true + }, + { + limit: 10, + display: displayTemplate, + templates: { suggestion: suggestionTemplate }, + source: function(query, sync) { + const lcSearch = query.toLowerCase(); + const results = searchIndex.query(function(q) { + q.term(lcSearch, { boost: 100 }); + q.term(lcSearch, { + boost: 10, + wildcard: lunr.Query.wildcard.TRAILING + }); + }).map(function(result) { + var doc = searchData[result.ref]; + doc.url = result.ref; + return doc; + }); + sync(results); + } + } + ); + $form.removeClass('loading'); + $typeahead.trigger('focus'); + }); + }); + + var baseURL = searchURL.slice(0, -"search.json".length); + + $typeahead.on('typeahead:select', function(e, result) { + window.location = baseURL + result.url; + }); +}); diff --git a/docs/1.9.0/AsyncHTTPClient/js/jquery.min.js b/docs/1.9.0/AsyncHTTPClient/js/jquery.min.js new file mode 100644 index 000000000..c4c6022f2 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 00){var c=e.utils.clone(r)||{};c.position=[a,l],c.index=s.length,s.push(new e.Token(i.slice(a,o),c))}a=o+1}}return s},e.tokenizer.separator=/[\s\-]+/,e.Pipeline=function(){this._stack=[]},e.Pipeline.registeredFunctions=Object.create(null),e.Pipeline.registerFunction=function(t,r){r in this.registeredFunctions&&e.utils.warn("Overwriting existing registered function: "+r),t.label=r,e.Pipeline.registeredFunctions[t.label]=t},e.Pipeline.warnIfFunctionNotRegistered=function(t){var r=t.label&&t.label in this.registeredFunctions;r||e.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",t)},e.Pipeline.load=function(t){var r=new e.Pipeline;return t.forEach(function(t){var i=e.Pipeline.registeredFunctions[t];if(!i)throw new Error("Cannot load unregistered function: "+t);r.add(i)}),r},e.Pipeline.prototype.add=function(){var t=Array.prototype.slice.call(arguments);t.forEach(function(t){e.Pipeline.warnIfFunctionNotRegistered(t),this._stack.push(t)},this)},e.Pipeline.prototype.after=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,r)},e.Pipeline.prototype.before=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");this._stack.splice(i,0,r)},e.Pipeline.prototype.remove=function(e){var t=this._stack.indexOf(e);t!=-1&&this._stack.splice(t,1)},e.Pipeline.prototype.run=function(e){for(var t=this._stack.length,r=0;r1&&(se&&(r=n),s!=e);)i=r-t,n=t+Math.floor(i/2),s=this.elements[2*n];return s==e?2*n:s>e?2*n:sa?l+=2:o==a&&(t+=r[u+1]*i[l+1],u+=2,l+=2);return t},e.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},e.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),t=1,r=0;t0){var o,a=s.str.charAt(0);a in s.node.edges?o=s.node.edges[a]:(o=new e.TokenSet,s.node.edges[a]=o),1==s.str.length&&(o["final"]=!0),n.push({node:o,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(0!=s.editsRemaining){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new e.TokenSet;s.node.edges["*"]=u}if(0==s.str.length&&(u["final"]=!0),n.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&n.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),1==s.str.length&&(s.node["final"]=!0),s.str.length>=1){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new e.TokenSet;s.node.edges["*"]=l}1==s.str.length&&(l["final"]=!0),n.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var c,h=s.str.charAt(0),d=s.str.charAt(1);d in s.node.edges?c=s.node.edges[d]:(c=new e.TokenSet,s.node.edges[d]=c),1==s.str.length&&(c["final"]=!0),n.push({node:c,editsRemaining:s.editsRemaining-1,str:h+s.str.slice(2)})}}}return i},e.TokenSet.fromString=function(t){for(var r=new e.TokenSet,i=r,n=0,s=t.length;n=e;t--){var r=this.uncheckedNodes[t],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r["char"]]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}},e.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},e.Index.prototype.search=function(t){return this.query(function(r){var i=new e.QueryParser(t,r);i.parse()})},e.Index.prototype.query=function(t){for(var r=new e.Query(this.fields),i=Object.create(null),n=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),u=0;u1?this._b=1:this._b=e},e.Builder.prototype.k1=function(e){this._k1=e},e.Builder.prototype.add=function(t,r){var i=t[this._ref],n=Object.keys(this._fields);this._documents[i]=r||{},this.documentCount+=1;for(var s=0;s=this.length)return e.QueryLexer.EOS;var t=this.str.charAt(this.pos);return this.pos+=1,t},e.QueryLexer.prototype.width=function(){return this.pos-this.start},e.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},e.QueryLexer.prototype.backup=function(){this.pos-=1},e.QueryLexer.prototype.acceptDigitRun=function(){var t,r;do t=this.next(),r=t.charCodeAt(0);while(r>47&&r<58);t!=e.QueryLexer.EOS&&this.backup()},e.QueryLexer.prototype.more=function(){return this.pos1&&(t.backup(),t.emit(e.QueryLexer.TERM)),t.ignore(),t.more())return e.QueryLexer.lexText},e.QueryLexer.lexEditDistance=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.EDIT_DISTANCE),e.QueryLexer.lexText},e.QueryLexer.lexBoost=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.BOOST),e.QueryLexer.lexText},e.QueryLexer.lexEOS=function(t){t.width()>0&&t.emit(e.QueryLexer.TERM)},e.QueryLexer.termSeparator=e.tokenizer.separator,e.QueryLexer.lexText=function(t){for(;;){var r=t.next();if(r==e.QueryLexer.EOS)return e.QueryLexer.lexEOS;if(92!=r.charCodeAt(0)){if(":"==r)return e.QueryLexer.lexField;if("~"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexEditDistance;if("^"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexBoost;if("+"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if("-"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if(r.match(e.QueryLexer.termSeparator))return e.QueryLexer.lexTerm}else t.escapeCharacter()}},e.QueryParser=function(t,r){this.lexer=new e.QueryLexer(t),this.query=r,this.currentClause={},this.lexemeIdx=0},e.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var t=e.QueryParser.parseClause;t;)t=t(this);return this.query},e.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},e.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},e.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},e.QueryParser.parseClause=function(t){var r=t.peekLexeme();if(void 0!=r)switch(r.type){case e.QueryLexer.PRESENCE:return e.QueryParser.parsePresence;case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expected either a field or a term, found "+r.type;throw r.str.length>=1&&(i+=" with value '"+r.str+"'"),new e.QueryParseError(i,r.start,r.end)}},e.QueryParser.parsePresence=function(t){var r=t.consumeLexeme();if(void 0!=r){switch(r.str){case"-":t.currentClause.presence=e.Query.presence.PROHIBITED;break;case"+":t.currentClause.presence=e.Query.presence.REQUIRED;break;default:var i="unrecognised presence operator'"+r.str+"'";throw new e.QueryParseError(i,r.start,r.end)}var n=t.peekLexeme();if(void 0==n){var i="expecting term or field, found nothing";throw new e.QueryParseError(i,r.start,r.end)}switch(n.type){case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expecting term or field, found '"+n.type+"'";throw new e.QueryParseError(i,n.start,n.end)}}},e.QueryParser.parseField=function(t){var r=t.consumeLexeme();if(void 0!=r){if(t.query.allFields.indexOf(r.str)==-1){var i=t.query.allFields.map(function(e){return"'"+e+"'"}).join(", "),n="unrecognised field '"+r.str+"', possible fields: "+i;throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.fields=[r.str];var s=t.peekLexeme();if(void 0==s){var n="expecting term, found nothing";throw new e.QueryParseError(n,r.start,r.end)}switch(s.type){case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var n="expecting term, found '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseTerm=function(t){var r=t.consumeLexeme();if(void 0!=r){t.currentClause.term=r.str.toLowerCase(),r.str.indexOf("*")!=-1&&(t.currentClause.usePipeline=!1);var i=t.peekLexeme();if(void 0==i)return void t.nextClause();switch(i.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+i.type+"'";throw new e.QueryParseError(n,i.start,i.end)}}},e.QueryParser.parseEditDistance=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="edit distance must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.editDistance=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseBoost=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="boost must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.boost=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():e.lunr=t()}(this,function(){return e})}(); diff --git a/docs/1.9.0/AsyncHTTPClient/js/typeahead.jquery.js b/docs/1.9.0/AsyncHTTPClient/js/typeahead.jquery.js new file mode 100644 index 000000000..3a2d2ab03 --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/js/typeahead.jquery.js @@ -0,0 +1,1694 @@ +/*! + * typeahead.js 1.3.1 + * https://github.com/corejavascript/typeahead.js + * Copyright 2013-2020 Twitter, Inc. and other contributors; Licensed MIT + */ + + +(function(root, factory) { + if (typeof define === "function" && define.amd) { + define([ "jquery" ], function(a0) { + return factory(a0); + }); + } else if (typeof module === "object" && module.exports) { + module.exports = factory(require("jquery")); + } else { + factory(root["jQuery"]); + } +})(this, function($) { + var _ = function() { + "use strict"; + return { + isMsie: function() { + return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false; + }, + isBlankString: function(str) { + return !str || /^\s*$/.test(str); + }, + escapeRegExChars: function(str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + }, + isString: function(obj) { + return typeof obj === "string"; + }, + isNumber: function(obj) { + return typeof obj === "number"; + }, + isArray: $.isArray, + isFunction: $.isFunction, + isObject: $.isPlainObject, + isUndefined: function(obj) { + return typeof obj === "undefined"; + }, + isElement: function(obj) { + return !!(obj && obj.nodeType === 1); + }, + isJQuery: function(obj) { + return obj instanceof $; + }, + toStr: function toStr(s) { + return _.isUndefined(s) || s === null ? "" : s + ""; + }, + bind: $.proxy, + each: function(collection, cb) { + $.each(collection, reverseArgs); + function reverseArgs(index, value) { + return cb(value, index); + } + }, + map: $.map, + filter: $.grep, + every: function(obj, test) { + var result = true; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (!(result = test.call(null, val, key, obj))) { + return false; + } + }); + return !!result; + }, + some: function(obj, test) { + var result = false; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (result = test.call(null, val, key, obj)) { + return false; + } + }); + return !!result; + }, + mixin: $.extend, + identity: function(x) { + return x; + }, + clone: function(obj) { + return $.extend(true, {}, obj); + }, + getIdGenerator: function() { + var counter = 0; + return function() { + return counter++; + }; + }, + templatify: function templatify(obj) { + return $.isFunction(obj) ? obj : template; + function template() { + return String(obj); + } + }, + defer: function(fn) { + setTimeout(fn, 0); + }, + debounce: function(func, wait, immediate) { + var timeout, result; + return function() { + var context = this, args = arguments, later, callNow; + later = function() { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + } + }; + callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + } + return result; + }; + }, + throttle: function(func, wait) { + var context, args, timeout, result, previous, later; + previous = 0; + later = function() { + previous = new Date(); + timeout = null; + result = func.apply(context, args); + }; + return function() { + var now = new Date(), remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + } else if (!timeout) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }, + stringify: function(val) { + return _.isString(val) ? val : JSON.stringify(val); + }, + guid: function() { + function _p8(s) { + var p = (Math.random().toString(16) + "000000000").substr(2, 8); + return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p; + } + return "tt-" + _p8() + _p8(true) + _p8(true) + _p8(); + }, + noop: function() {} + }; + }(); + var WWW = function() { + "use strict"; + var defaultClassNames = { + wrapper: "twitter-typeahead", + input: "tt-input", + hint: "tt-hint", + menu: "tt-menu", + dataset: "tt-dataset", + suggestion: "tt-suggestion", + selectable: "tt-selectable", + empty: "tt-empty", + open: "tt-open", + cursor: "tt-cursor", + highlight: "tt-highlight" + }; + return build; + function build(o) { + var www, classes; + classes = _.mixin({}, defaultClassNames, o); + www = { + css: buildCss(), + classes: classes, + html: buildHtml(classes), + selectors: buildSelectors(classes) + }; + return { + css: www.css, + html: www.html, + classes: www.classes, + selectors: www.selectors, + mixin: function(o) { + _.mixin(o, www); + } + }; + } + function buildHtml(c) { + return { + wrapper: '', + menu: '
' + }; + } + function buildSelectors(classes) { + var selectors = {}; + _.each(classes, function(v, k) { + selectors[k] = "." + v; + }); + return selectors; + } + function buildCss() { + var css = { + wrapper: { + position: "relative", + display: "inline-block" + }, + hint: { + position: "absolute", + top: "0", + left: "0", + borderColor: "transparent", + boxShadow: "none", + opacity: "1" + }, + input: { + position: "relative", + verticalAlign: "top", + backgroundColor: "transparent" + }, + inputWithNoHint: { + position: "relative", + verticalAlign: "top" + }, + menu: { + position: "absolute", + top: "100%", + left: "0", + zIndex: "100", + display: "none" + }, + ltr: { + left: "0", + right: "auto" + }, + rtl: { + left: "auto", + right: " 0" + } + }; + if (_.isMsie()) { + _.mixin(css.input, { + backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" + }); + } + return css; + } + }(); + var EventBus = function() { + "use strict"; + var namespace, deprecationMap; + namespace = "typeahead:"; + deprecationMap = { + render: "rendered", + cursorchange: "cursorchanged", + select: "selected", + autocomplete: "autocompleted" + }; + function EventBus(o) { + if (!o || !o.el) { + $.error("EventBus initialized without el"); + } + this.$el = $(o.el); + } + _.mixin(EventBus.prototype, { + _trigger: function(type, args) { + var $e = $.Event(namespace + type); + this.$el.trigger.call(this.$el, $e, args || []); + return $e; + }, + before: function(type) { + var args, $e; + args = [].slice.call(arguments, 1); + $e = this._trigger("before" + type, args); + return $e.isDefaultPrevented(); + }, + trigger: function(type) { + var deprecatedType; + this._trigger(type, [].slice.call(arguments, 1)); + if (deprecatedType = deprecationMap[type]) { + this._trigger(deprecatedType, [].slice.call(arguments, 1)); + } + } + }); + return EventBus; + }(); + var EventEmitter = function() { + "use strict"; + var splitter = /\s+/, nextTick = getNextTick(); + return { + onSync: onSync, + onAsync: onAsync, + off: off, + trigger: trigger + }; + function on(method, types, cb, context) { + var type; + if (!cb) { + return this; + } + types = types.split(splitter); + cb = context ? bindContext(cb, context) : cb; + this._callbacks = this._callbacks || {}; + while (type = types.shift()) { + this._callbacks[type] = this._callbacks[type] || { + sync: [], + async: [] + }; + this._callbacks[type][method].push(cb); + } + return this; + } + function onAsync(types, cb, context) { + return on.call(this, "async", types, cb, context); + } + function onSync(types, cb, context) { + return on.call(this, "sync", types, cb, context); + } + function off(types) { + var type; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + while (type = types.shift()) { + delete this._callbacks[type]; + } + return this; + } + function trigger(types) { + var type, callbacks, args, syncFlush, asyncFlush; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + args = [].slice.call(arguments, 1); + while ((type = types.shift()) && (callbacks = this._callbacks[type])) { + syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args)); + asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args)); + syncFlush() && nextTick(asyncFlush); + } + return this; + } + function getFlush(callbacks, context, args) { + return flush; + function flush() { + var cancelled; + for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) { + cancelled = callbacks[i].apply(context, args) === false; + } + return !cancelled; + } + } + function getNextTick() { + var nextTickFn; + if (window.setImmediate) { + nextTickFn = function nextTickSetImmediate(fn) { + setImmediate(function() { + fn(); + }); + }; + } else { + nextTickFn = function nextTickSetTimeout(fn) { + setTimeout(function() { + fn(); + }, 0); + }; + } + return nextTickFn; + } + function bindContext(fn, context) { + return fn.bind ? fn.bind(context) : function() { + fn.apply(context, [].slice.call(arguments, 0)); + }; + } + }(); + var highlight = function(doc) { + "use strict"; + var defaults = { + node: null, + pattern: null, + tagName: "strong", + className: null, + wordsOnly: false, + caseSensitive: false, + diacriticInsensitive: false + }; + var accented = { + A: "[AaªÀ-Åà-åĀ-ąǍǎȀ-ȃȦȧᴬᵃḀḁẚẠ-ảₐ℀℁℻⒜Ⓐⓐ㍱-㍴㎀-㎄㎈㎉㎩-㎯㏂㏊㏟㏿Aa]", + B: "[BbᴮᵇḂ-ḇℬ⒝Ⓑⓑ㍴㎅-㎇㏃㏈㏔㏝Bb]", + C: "[CcÇçĆ-čᶜ℀ℂ℃℅℆ℭⅭⅽ⒞Ⓒⓒ㍶㎈㎉㎝㎠㎤㏄-㏇Cc]", + D: "[DdĎďDŽ-džDZ-dzᴰᵈḊ-ḓⅅⅆⅮⅾ⒟Ⓓⓓ㋏㍲㍷-㍹㎗㎭-㎯㏅㏈Dd]", + E: "[EeÈ-Ëè-ëĒ-ěȄ-ȇȨȩᴱᵉḘ-ḛẸ-ẽₑ℡ℯℰⅇ⒠Ⓔⓔ㉐㋍㋎Ee]", + F: "[FfᶠḞḟ℉ℱ℻⒡Ⓕⓕ㎊-㎌㎙ff-fflFf]", + G: "[GgĜ-ģǦǧǴǵᴳᵍḠḡℊ⒢Ⓖⓖ㋌㋍㎇㎍-㎏㎓㎬㏆㏉㏒㏿Gg]", + H: "[HhĤĥȞȟʰᴴḢ-ḫẖℋ-ℎ⒣Ⓗⓗ㋌㍱㎐-㎔㏊㏋㏗Hh]", + I: "[IiÌ-Ïì-ïĨ-İIJijǏǐȈ-ȋᴵᵢḬḭỈ-ịⁱℐℑℹⅈⅠ-ⅣⅥ-ⅨⅪⅫⅰ-ⅳⅵ-ⅸⅺⅻ⒤Ⓘⓘ㍺㏌㏕fiffiIi]", + J: "[JjIJ-ĵLJ-njǰʲᴶⅉ⒥ⒿⓙⱼJj]", + K: "[KkĶķǨǩᴷᵏḰ-ḵK⒦Ⓚⓚ㎄㎅㎉㎏㎑㎘㎞㎢㎦㎪㎸㎾㏀㏆㏍-㏏Kk]", + L: "[LlĹ-ŀLJ-ljˡᴸḶḷḺ-ḽℒℓ℡Ⅼⅼ⒧Ⓛⓛ㋏㎈㎉㏐-㏓㏕㏖㏿flfflLl]", + M: "[MmᴹᵐḾ-ṃ℠™ℳⅯⅿ⒨Ⓜⓜ㍷-㍹㎃㎆㎎㎒㎖㎙-㎨㎫㎳㎷㎹㎽㎿㏁㏂㏎㏐㏔-㏖㏘㏙㏞㏟Mm]", + N: "[NnÑñŃ-ʼnNJ-njǸǹᴺṄ-ṋⁿℕ№⒩Ⓝⓝ㎁㎋㎚㎱㎵㎻㏌㏑Nn]", + O: "[OoºÒ-Öò-öŌ-őƠơǑǒǪǫȌ-ȏȮȯᴼᵒỌ-ỏₒ℅№ℴ⒪Ⓞⓞ㍵㏇㏒㏖Oo]", + P: "[PpᴾᵖṔ-ṗℙ⒫Ⓟⓟ㉐㍱㍶㎀㎊㎩-㎬㎰㎴㎺㏋㏗-㏚Pp]", + Q: "[Qqℚ⒬Ⓠⓠ㏃Qq]", + R: "[RrŔ-řȐ-ȓʳᴿᵣṘ-ṛṞṟ₨ℛ-ℝ⒭Ⓡⓡ㋍㍴㎭-㎯㏚㏛Rr]", + S: "[SsŚ-šſȘșˢṠ-ṣ₨℁℠⒮Ⓢⓢ㎧㎨㎮-㎳㏛㏜stSs]", + T: "[TtŢ-ťȚțᵀᵗṪ-ṱẗ℡™⒯Ⓣⓣ㉐㋏㎔㏏ſtstTt]", + U: "[UuÙ-Üù-üŨ-ųƯưǓǔȔ-ȗᵁᵘᵤṲ-ṷỤ-ủ℆⒰Ⓤⓤ㍳㍺Uu]", + V: "[VvᵛᵥṼ-ṿⅣ-Ⅷⅳ-ⅷ⒱Ⓥⓥⱽ㋎㍵㎴-㎹㏜㏞Vv]", + W: "[WwŴŵʷᵂẀ-ẉẘ⒲Ⓦⓦ㎺-㎿㏝Ww]", + X: "[XxˣẊ-ẍₓ℻Ⅸ-Ⅻⅸ-ⅻ⒳Ⓧⓧ㏓Xx]", + Y: "[YyÝýÿŶ-ŸȲȳʸẎẏẙỲ-ỹ⒴Ⓨⓨ㏉Yy]", + Z: "[ZzŹ-žDZ-dzᶻẐ-ẕℤℨ⒵Ⓩⓩ㎐-㎔Zz]" + }; + return function hightlight(o) { + var regex; + o = _.mixin({}, defaults, o); + if (!o.node || !o.pattern) { + return; + } + o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; + regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly, o.diacriticInsensitive); + traverse(o.node, hightlightTextNode); + function hightlightTextNode(textNode) { + var match, patternNode, wrapperNode; + if (match = regex.exec(textNode.data)) { + wrapperNode = doc.createElement(o.tagName); + o.className && (wrapperNode.className = o.className); + patternNode = textNode.splitText(match.index); + patternNode.splitText(match[0].length); + wrapperNode.appendChild(patternNode.cloneNode(true)); + textNode.parentNode.replaceChild(wrapperNode, patternNode); + } + return !!match; + } + function traverse(el, hightlightTextNode) { + var childNode, TEXT_NODE_TYPE = 3; + for (var i = 0; i < el.childNodes.length; i++) { + childNode = el.childNodes[i]; + if (childNode.nodeType === TEXT_NODE_TYPE) { + i += hightlightTextNode(childNode) ? 1 : 0; + } else { + traverse(childNode, hightlightTextNode); + } + } + } + }; + function accent_replacer(chr) { + return accented[chr.toUpperCase()] || chr; + } + function getRegex(patterns, caseSensitive, wordsOnly, diacriticInsensitive) { + var escapedPatterns = [], regexStr; + for (var i = 0, len = patterns.length; i < len; i++) { + var escapedWord = _.escapeRegExChars(patterns[i]); + if (diacriticInsensitive) { + escapedWord = escapedWord.replace(/\S/g, accent_replacer); + } + escapedPatterns.push(escapedWord); + } + regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")"; + return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); + } + }(window.document); + var Input = function() { + "use strict"; + var specialKeyCodeMap; + specialKeyCodeMap = { + 9: "tab", + 27: "esc", + 37: "left", + 39: "right", + 13: "enter", + 38: "up", + 40: "down" + }; + function Input(o, www) { + var id; + o = o || {}; + if (!o.input) { + $.error("input is missing"); + } + www.mixin(this); + this.$hint = $(o.hint); + this.$input = $(o.input); + this.$menu = $(o.menu); + id = this.$input.attr("id") || _.guid(); + this.$menu.attr("id", id + "_listbox"); + this.$hint.attr({ + "aria-hidden": true + }); + this.$input.attr({ + "aria-owns": id + "_listbox", + role: "combobox", + "aria-autocomplete": "list", + "aria-expanded": false + }); + this.query = this.$input.val(); + this.queryWhenFocused = this.hasFocus() ? this.query : null; + this.$overflowHelper = buildOverflowHelper(this.$input); + this._checkLanguageDirection(); + if (this.$hint.length === 0) { + this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; + } + this.onSync("cursorchange", this._updateDescendent); + } + Input.normalizeQuery = function(str) { + return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " "); + }; + _.mixin(Input.prototype, EventEmitter, { + _onBlur: function onBlur() { + this.resetInputValue(); + this.trigger("blurred"); + }, + _onFocus: function onFocus() { + this.queryWhenFocused = this.query; + this.trigger("focused"); + }, + _onKeydown: function onKeydown($e) { + var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; + this._managePreventDefault(keyName, $e); + if (keyName && this._shouldTrigger(keyName, $e)) { + this.trigger(keyName + "Keyed", $e); + } + }, + _onInput: function onInput() { + this._setQuery(this.getInputValue()); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + _managePreventDefault: function managePreventDefault(keyName, $e) { + var preventDefault; + switch (keyName) { + case "up": + case "down": + preventDefault = !withModifier($e); + break; + + default: + preventDefault = false; + } + preventDefault && $e.preventDefault(); + }, + _shouldTrigger: function shouldTrigger(keyName, $e) { + var trigger; + switch (keyName) { + case "tab": + trigger = !withModifier($e); + break; + + default: + trigger = true; + } + return trigger; + }, + _checkLanguageDirection: function checkLanguageDirection() { + var dir = (this.$input.css("direction") || "ltr").toLowerCase(); + if (this.dir !== dir) { + this.dir = dir; + this.$hint.attr("dir", dir); + this.trigger("langDirChanged", dir); + } + }, + _setQuery: function setQuery(val, silent) { + var areEquivalent, hasDifferentWhitespace; + areEquivalent = areQueriesEquivalent(val, this.query); + hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false; + this.query = val; + if (!silent && !areEquivalent) { + this.trigger("queryChanged", this.query); + } else if (!silent && hasDifferentWhitespace) { + this.trigger("whitespaceChanged", this.query); + } + }, + _updateDescendent: function updateDescendent(event, id) { + this.$input.attr("aria-activedescendant", id); + }, + bind: function() { + var that = this, onBlur, onFocus, onKeydown, onInput; + onBlur = _.bind(this._onBlur, this); + onFocus = _.bind(this._onFocus, this); + onKeydown = _.bind(this._onKeydown, this); + onInput = _.bind(this._onInput, this); + this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown); + if (!_.isMsie() || _.isMsie() > 9) { + this.$input.on("input.tt", onInput); + } else { + this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { + if (specialKeyCodeMap[$e.which || $e.keyCode]) { + return; + } + _.defer(_.bind(that._onInput, that, $e)); + }); + } + return this; + }, + focus: function focus() { + this.$input.focus(); + }, + blur: function blur() { + this.$input.blur(); + }, + getLangDir: function getLangDir() { + return this.dir; + }, + getQuery: function getQuery() { + return this.query || ""; + }, + setQuery: function setQuery(val, silent) { + this.setInputValue(val); + this._setQuery(val, silent); + }, + hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() { + return this.query !== this.queryWhenFocused; + }, + getInputValue: function getInputValue() { + return this.$input.val(); + }, + setInputValue: function setInputValue(value) { + this.$input.val(value); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + resetInputValue: function resetInputValue() { + this.setInputValue(this.query); + }, + getHint: function getHint() { + return this.$hint.val(); + }, + setHint: function setHint(value) { + this.$hint.val(value); + }, + clearHint: function clearHint() { + this.setHint(""); + }, + clearHintIfInvalid: function clearHintIfInvalid() { + var val, hint, valIsPrefixOfHint, isValid; + val = this.getInputValue(); + hint = this.getHint(); + valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; + isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow(); + !isValid && this.clearHint(); + }, + hasFocus: function hasFocus() { + return this.$input.is(":focus"); + }, + hasOverflow: function hasOverflow() { + var constraint = this.$input.width() - 2; + this.$overflowHelper.text(this.getInputValue()); + return this.$overflowHelper.width() >= constraint; + }, + isCursorAtEnd: function() { + var valueLength, selectionStart, range; + valueLength = this.$input.val().length; + selectionStart = this.$input[0].selectionStart; + if (_.isNumber(selectionStart)) { + return selectionStart === valueLength; + } else if (document.selection) { + range = document.selection.createRange(); + range.moveStart("character", -valueLength); + return valueLength === range.text.length; + } + return true; + }, + destroy: function destroy() { + this.$hint.off(".tt"); + this.$input.off(".tt"); + this.$overflowHelper.remove(); + this.$hint = this.$input = this.$overflowHelper = $("
"); + }, + setAriaExpanded: function setAriaExpanded(value) { + this.$input.attr("aria-expanded", value); + } + }); + return Input; + function buildOverflowHelper($input) { + return $('').css({ + position: "absolute", + visibility: "hidden", + whiteSpace: "pre", + fontFamily: $input.css("font-family"), + fontSize: $input.css("font-size"), + fontStyle: $input.css("font-style"), + fontVariant: $input.css("font-variant"), + fontWeight: $input.css("font-weight"), + wordSpacing: $input.css("word-spacing"), + letterSpacing: $input.css("letter-spacing"), + textIndent: $input.css("text-indent"), + textRendering: $input.css("text-rendering"), + textTransform: $input.css("text-transform") + }).insertAfter($input); + } + function areQueriesEquivalent(a, b) { + return Input.normalizeQuery(a) === Input.normalizeQuery(b); + } + function withModifier($e) { + return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; + } + }(); + var Dataset = function() { + "use strict"; + var keys, nameGenerator; + keys = { + dataset: "tt-selectable-dataset", + val: "tt-selectable-display", + obj: "tt-selectable-object" + }; + nameGenerator = _.getIdGenerator(); + function Dataset(o, www) { + o = o || {}; + o.templates = o.templates || {}; + o.templates.notFound = o.templates.notFound || o.templates.empty; + if (!o.source) { + $.error("missing source"); + } + if (!o.node) { + $.error("missing node"); + } + if (o.name && !isValidName(o.name)) { + $.error("invalid dataset name: " + o.name); + } + www.mixin(this); + this.highlight = !!o.highlight; + this.name = _.toStr(o.name || nameGenerator()); + this.limit = o.limit || 5; + this.displayFn = getDisplayFn(o.display || o.displayKey); + this.templates = getTemplates(o.templates, this.displayFn); + this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source; + this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async; + this._resetLastSuggestion(); + this.$el = $(o.node).attr("role", "presentation").addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); + } + Dataset.extractData = function extractData(el) { + var $el = $(el); + if ($el.data(keys.obj)) { + return { + dataset: $el.data(keys.dataset) || "", + val: $el.data(keys.val) || "", + obj: $el.data(keys.obj) || null + }; + } + return null; + }; + _.mixin(Dataset.prototype, EventEmitter, { + _overwrite: function overwrite(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (this.async && this.templates.pending) { + this._renderPending(query); + } else if (!this.async && this.templates.notFound) { + this._renderNotFound(query); + } else { + this._empty(); + } + this.trigger("rendered", suggestions, false, this.name); + }, + _append: function append(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length && this.$lastSuggestion.length) { + this._appendSuggestions(query, suggestions); + } else if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (!this.$lastSuggestion.length && this.templates.notFound) { + this._renderNotFound(query); + } + this.trigger("rendered", suggestions, true, this.name); + }, + _renderSuggestions: function renderSuggestions(query, suggestions) { + var $fragment; + $fragment = this._getSuggestionsFragment(query, suggestions); + this.$lastSuggestion = $fragment.children().last(); + this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions)); + }, + _appendSuggestions: function appendSuggestions(query, suggestions) { + var $fragment, $lastSuggestion; + $fragment = this._getSuggestionsFragment(query, suggestions); + $lastSuggestion = $fragment.children().last(); + this.$lastSuggestion.after($fragment); + this.$lastSuggestion = $lastSuggestion; + }, + _renderPending: function renderPending(query) { + var template = this.templates.pending; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _renderNotFound: function renderNotFound(query) { + var template = this.templates.notFound; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _empty: function empty() { + this.$el.empty(); + this._resetLastSuggestion(); + }, + _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) { + var that = this, fragment; + fragment = document.createDocumentFragment(); + _.each(suggestions, function getSuggestionNode(suggestion) { + var $el, context; + context = that._injectQuery(query, suggestion); + $el = $(that.templates.suggestion(context)).data(keys.dataset, that.name).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable); + fragment.appendChild($el[0]); + }); + this.highlight && highlight({ + className: this.classes.highlight, + node: fragment, + pattern: query + }); + return $(fragment); + }, + _getFooter: function getFooter(query, suggestions) { + return this.templates.footer ? this.templates.footer({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _getHeader: function getHeader(query, suggestions) { + return this.templates.header ? this.templates.header({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _resetLastSuggestion: function resetLastSuggestion() { + this.$lastSuggestion = $(); + }, + _injectQuery: function injectQuery(query, obj) { + return _.isObject(obj) ? _.mixin({ + _query: query + }, obj) : obj; + }, + update: function update(query) { + var that = this, canceled = false, syncCalled = false, rendered = 0; + this.cancel(); + this.cancel = function cancel() { + canceled = true; + that.cancel = $.noop; + that.async && that.trigger("asyncCanceled", query, that.name); + }; + this.source(query, sync, async); + !syncCalled && sync([]); + function sync(suggestions) { + if (syncCalled) { + return; + } + syncCalled = true; + suggestions = (suggestions || []).slice(0, that.limit); + rendered = suggestions.length; + that._overwrite(query, suggestions); + if (rendered < that.limit && that.async) { + that.trigger("asyncRequested", query, that.name); + } + } + function async(suggestions) { + suggestions = suggestions || []; + if (!canceled && rendered < that.limit) { + that.cancel = $.noop; + var idx = Math.abs(rendered - that.limit); + rendered += idx; + that._append(query, suggestions.slice(0, idx)); + that.async && that.trigger("asyncReceived", query, that.name); + } + } + }, + cancel: $.noop, + clear: function clear() { + this._empty(); + this.cancel(); + this.trigger("cleared"); + }, + isEmpty: function isEmpty() { + return this.$el.is(":empty"); + }, + destroy: function destroy() { + this.$el = $("
"); + } + }); + return Dataset; + function getDisplayFn(display) { + display = display || _.stringify; + return _.isFunction(display) ? display : displayFn; + function displayFn(obj) { + return obj[display]; + } + } + function getTemplates(templates, displayFn) { + return { + notFound: templates.notFound && _.templatify(templates.notFound), + pending: templates.pending && _.templatify(templates.pending), + header: templates.header && _.templatify(templates.header), + footer: templates.footer && _.templatify(templates.footer), + suggestion: templates.suggestion ? userSuggestionTemplate : suggestionTemplate + }; + function userSuggestionTemplate(context) { + var template = templates.suggestion; + return $(template(context)).attr("id", _.guid()); + } + function suggestionTemplate(context) { + return $('
').attr("id", _.guid()).text(displayFn(context)); + } + } + function isValidName(str) { + return /^[_a-zA-Z0-9-]+$/.test(str); + } + }(); + var Menu = function() { + "use strict"; + function Menu(o, www) { + var that = this; + o = o || {}; + if (!o.node) { + $.error("node is required"); + } + www.mixin(this); + this.$node = $(o.node); + this.query = null; + this.datasets = _.map(o.datasets, initializeDataset); + function initializeDataset(oDataset) { + var node = that.$node.find(oDataset.node).first(); + oDataset.node = node.length ? node : $("
").appendTo(that.$node); + return new Dataset(oDataset, www); + } + } + _.mixin(Menu.prototype, EventEmitter, { + _onSelectableClick: function onSelectableClick($e) { + this.trigger("selectableClicked", $($e.currentTarget)); + }, + _onRendered: function onRendered(type, dataset, suggestions, async) { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetRendered", dataset, suggestions, async); + }, + _onCleared: function onCleared() { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetCleared"); + }, + _propagate: function propagate() { + this.trigger.apply(this, arguments); + }, + _allDatasetsEmpty: function allDatasetsEmpty() { + return _.every(this.datasets, _.bind(function isDatasetEmpty(dataset) { + var isEmpty = dataset.isEmpty(); + this.$node.attr("aria-expanded", !isEmpty); + return isEmpty; + }, this)); + }, + _getSelectables: function getSelectables() { + return this.$node.find(this.selectors.selectable); + }, + _removeCursor: function _removeCursor() { + var $selectable = this.getActiveSelectable(); + $selectable && $selectable.removeClass(this.classes.cursor); + }, + _ensureVisible: function ensureVisible($el) { + var elTop, elBottom, nodeScrollTop, nodeHeight; + elTop = $el.position().top; + elBottom = elTop + $el.outerHeight(true); + nodeScrollTop = this.$node.scrollTop(); + nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10); + if (elTop < 0) { + this.$node.scrollTop(nodeScrollTop + elTop); + } else if (nodeHeight < elBottom) { + this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight)); + } + }, + bind: function() { + var that = this, onSelectableClick; + onSelectableClick = _.bind(this._onSelectableClick, this); + this.$node.on("click.tt", this.selectors.selectable, onSelectableClick); + this.$node.on("mouseover", this.selectors.selectable, function() { + that.setCursor($(this)); + }); + this.$node.on("mouseleave", function() { + that._removeCursor(); + }); + _.each(this.datasets, function(dataset) { + dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that); + }); + return this; + }, + isOpen: function isOpen() { + return this.$node.hasClass(this.classes.open); + }, + open: function open() { + this.$node.scrollTop(0); + this.$node.addClass(this.classes.open); + }, + close: function close() { + this.$node.attr("aria-expanded", false); + this.$node.removeClass(this.classes.open); + this._removeCursor(); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.attr("dir", dir); + }, + selectableRelativeToCursor: function selectableRelativeToCursor(delta) { + var $selectables, $oldCursor, oldIndex, newIndex; + $oldCursor = this.getActiveSelectable(); + $selectables = this._getSelectables(); + oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1; + newIndex = oldIndex + delta; + newIndex = (newIndex + 1) % ($selectables.length + 1) - 1; + newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex; + return newIndex === -1 ? null : $selectables.eq(newIndex); + }, + setCursor: function setCursor($selectable) { + this._removeCursor(); + if ($selectable = $selectable && $selectable.first()) { + $selectable.addClass(this.classes.cursor); + this._ensureVisible($selectable); + } + }, + getSelectableData: function getSelectableData($el) { + return $el && $el.length ? Dataset.extractData($el) : null; + }, + getActiveSelectable: function getActiveSelectable() { + var $selectable = this._getSelectables().filter(this.selectors.cursor).first(); + return $selectable.length ? $selectable : null; + }, + getTopSelectable: function getTopSelectable() { + var $selectable = this._getSelectables().first(); + return $selectable.length ? $selectable : null; + }, + update: function update(query) { + var isValidUpdate = query !== this.query; + if (isValidUpdate) { + this.query = query; + _.each(this.datasets, updateDataset); + } + return isValidUpdate; + function updateDataset(dataset) { + dataset.update(query); + } + }, + empty: function empty() { + _.each(this.datasets, clearDataset); + this.query = null; + this.$node.addClass(this.classes.empty); + function clearDataset(dataset) { + dataset.clear(); + } + }, + destroy: function destroy() { + this.$node.off(".tt"); + this.$node = $("
"); + _.each(this.datasets, destroyDataset); + function destroyDataset(dataset) { + dataset.destroy(); + } + } + }); + return Menu; + }(); + var Status = function() { + "use strict"; + function Status(options) { + this.$el = $("", { + role: "status", + "aria-live": "polite" + }).css({ + position: "absolute", + padding: "0", + border: "0", + height: "1px", + width: "1px", + "margin-bottom": "-1px", + "margin-right": "-1px", + overflow: "hidden", + clip: "rect(0 0 0 0)", + "white-space": "nowrap" + }); + options.$input.after(this.$el); + _.each(options.menu.datasets, _.bind(function(dataset) { + if (dataset.onSync) { + dataset.onSync("rendered", _.bind(this.update, this)); + dataset.onSync("cleared", _.bind(this.cleared, this)); + } + }, this)); + } + _.mixin(Status.prototype, { + update: function update(event, suggestions) { + var length = suggestions.length; + var words; + if (length === 1) { + words = { + result: "result", + is: "is" + }; + } else { + words = { + result: "results", + is: "are" + }; + } + this.$el.text(length + " " + words.result + " " + words.is + " available, use up and down arrow keys to navigate."); + }, + cleared: function() { + this.$el.text(""); + } + }); + return Status; + }(); + var DefaultMenu = function() { + "use strict"; + var s = Menu.prototype; + function DefaultMenu() { + Menu.apply(this, [].slice.call(arguments, 0)); + } + _.mixin(DefaultMenu.prototype, Menu.prototype, { + open: function open() { + !this._allDatasetsEmpty() && this._show(); + return s.open.apply(this, [].slice.call(arguments, 0)); + }, + close: function close() { + this._hide(); + return s.close.apply(this, [].slice.call(arguments, 0)); + }, + _onRendered: function onRendered() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onRendered.apply(this, [].slice.call(arguments, 0)); + }, + _onCleared: function onCleared() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onCleared.apply(this, [].slice.call(arguments, 0)); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl); + return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0)); + }, + _hide: function hide() { + this.$node.hide(); + }, + _show: function show() { + this.$node.css("display", "block"); + } + }); + return DefaultMenu; + }(); + var Typeahead = function() { + "use strict"; + function Typeahead(o, www) { + var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged; + o = o || {}; + if (!o.input) { + $.error("missing input"); + } + if (!o.menu) { + $.error("missing menu"); + } + if (!o.eventBus) { + $.error("missing event bus"); + } + www.mixin(this); + this.eventBus = o.eventBus; + this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; + this.input = o.input; + this.menu = o.menu; + this.enabled = true; + this.autoselect = !!o.autoselect; + this.active = false; + this.input.hasFocus() && this.activate(); + this.dir = this.input.getLangDir(); + this._hacks(); + this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this); + onFocused = c(this, "activate", "open", "_onFocused"); + onBlurred = c(this, "deactivate", "_onBlurred"); + onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed"); + onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed"); + onEscKeyed = c(this, "isActive", "_onEscKeyed"); + onUpKeyed = c(this, "isActive", "open", "_onUpKeyed"); + onDownKeyed = c(this, "isActive", "open", "_onDownKeyed"); + onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed"); + onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed"); + onQueryChanged = c(this, "_openIfActive", "_onQueryChanged"); + onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged"); + this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this); + } + _.mixin(Typeahead.prototype, { + _hacks: function hacks() { + var $input, $menu; + $input = this.input.$input || $("
"); + $menu = this.menu.$node || $("
"); + $input.on("blur.tt", function($e) { + var active, isActive, hasActive; + active = document.activeElement; + isActive = $menu.is(active); + hasActive = $menu.has(active).length > 0; + if (_.isMsie() && (isActive || hasActive)) { + $e.preventDefault(); + $e.stopImmediatePropagation(); + _.defer(function() { + $input.focus(); + }); + } + }); + $menu.on("mousedown.tt", function($e) { + $e.preventDefault(); + }); + }, + _onSelectableClicked: function onSelectableClicked(type, $el) { + this.select($el); + }, + _onDatasetCleared: function onDatasetCleared() { + this._updateHint(); + }, + _onDatasetRendered: function onDatasetRendered(type, suggestions, async, dataset) { + this._updateHint(); + if (this.autoselect) { + var cursorClass = this.selectors.cursor.substr(1); + this.menu.$node.find(this.selectors.suggestion).first().addClass(cursorClass); + } + this.eventBus.trigger("render", suggestions, async, dataset); + }, + _onAsyncRequested: function onAsyncRequested(type, dataset, query) { + this.eventBus.trigger("asyncrequest", query, dataset); + }, + _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) { + this.eventBus.trigger("asynccancel", query, dataset); + }, + _onAsyncReceived: function onAsyncReceived(type, dataset, query) { + this.eventBus.trigger("asyncreceive", query, dataset); + }, + _onFocused: function onFocused() { + this._minLengthMet() && this.menu.update(this.input.getQuery()); + }, + _onBlurred: function onBlurred() { + if (this.input.hasQueryChangedSinceLastFocus()) { + this.eventBus.trigger("change", this.input.getQuery()); + } + }, + _onEnterKeyed: function onEnterKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + if (this.select($selectable)) { + $e.preventDefault(); + $e.stopPropagation(); + } + } else if (this.autoselect) { + if (this.select(this.menu.getTopSelectable())) { + $e.preventDefault(); + $e.stopPropagation(); + } + } + }, + _onTabKeyed: function onTabKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + this.select($selectable) && $e.preventDefault(); + } else if (this.autoselect) { + if ($selectable = this.menu.getTopSelectable()) { + this.autocomplete($selectable) && $e.preventDefault(); + } + } + }, + _onEscKeyed: function onEscKeyed() { + this.close(); + }, + _onUpKeyed: function onUpKeyed() { + this.moveCursor(-1); + }, + _onDownKeyed: function onDownKeyed() { + this.moveCursor(+1); + }, + _onLeftKeyed: function onLeftKeyed() { + if (this.dir === "rtl" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); + } + }, + _onRightKeyed: function onRightKeyed() { + if (this.dir === "ltr" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); + } + }, + _onQueryChanged: function onQueryChanged(e, query) { + this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty(); + }, + _onWhitespaceChanged: function onWhitespaceChanged() { + this._updateHint(); + }, + _onLangDirChanged: function onLangDirChanged(e, dir) { + if (this.dir !== dir) { + this.dir = dir; + this.menu.setLanguageDirection(dir); + } + }, + _openIfActive: function openIfActive() { + this.isActive() && this.open(); + }, + _minLengthMet: function minLengthMet(query) { + query = _.isString(query) ? query : this.input.getQuery() || ""; + return query.length >= this.minLength; + }, + _updateHint: function updateHint() { + var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match; + $selectable = this.menu.getTopSelectable(); + data = this.menu.getSelectableData($selectable); + val = this.input.getInputValue(); + if (data && !_.isBlankString(val) && !this.input.hasOverflow()) { + query = Input.normalizeQuery(val); + escapedQuery = _.escapeRegExChars(query); + frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i"); + match = frontMatchRegEx.exec(data.val); + match && this.input.setHint(val + match[1]); + } else { + this.input.clearHint(); + } + }, + isEnabled: function isEnabled() { + return this.enabled; + }, + enable: function enable() { + this.enabled = true; + }, + disable: function disable() { + this.enabled = false; + }, + isActive: function isActive() { + return this.active; + }, + activate: function activate() { + if (this.isActive()) { + return true; + } else if (!this.isEnabled() || this.eventBus.before("active")) { + return false; + } else { + this.active = true; + this.eventBus.trigger("active"); + return true; + } + }, + deactivate: function deactivate() { + if (!this.isActive()) { + return true; + } else if (this.eventBus.before("idle")) { + return false; + } else { + this.active = false; + this.close(); + this.eventBus.trigger("idle"); + return true; + } + }, + isOpen: function isOpen() { + return this.menu.isOpen(); + }, + open: function open() { + if (!this.isOpen() && !this.eventBus.before("open")) { + this.input.setAriaExpanded(true); + this.menu.open(); + this._updateHint(); + this.eventBus.trigger("open"); + } + return this.isOpen(); + }, + close: function close() { + if (this.isOpen() && !this.eventBus.before("close")) { + this.input.setAriaExpanded(false); + this.menu.close(); + this.input.clearHint(); + this.input.resetInputValue(); + this.eventBus.trigger("close"); + } + return !this.isOpen(); + }, + setVal: function setVal(val) { + this.input.setQuery(_.toStr(val)); + }, + getVal: function getVal() { + return this.input.getQuery(); + }, + select: function select($selectable) { + var data = this.menu.getSelectableData($selectable); + if (data && !this.eventBus.before("select", data.obj, data.dataset)) { + this.input.setQuery(data.val, true); + this.eventBus.trigger("select", data.obj, data.dataset); + this.close(); + return true; + } + return false; + }, + autocomplete: function autocomplete($selectable) { + var query, data, isValid; + query = this.input.getQuery(); + data = this.menu.getSelectableData($selectable); + isValid = data && query !== data.val; + if (isValid && !this.eventBus.before("autocomplete", data.obj, data.dataset)) { + this.input.setQuery(data.val); + this.eventBus.trigger("autocomplete", data.obj, data.dataset); + return true; + } + return false; + }, + moveCursor: function moveCursor(delta) { + var query, $candidate, data, suggestion, datasetName, cancelMove, id; + query = this.input.getQuery(); + $candidate = this.menu.selectableRelativeToCursor(delta); + data = this.menu.getSelectableData($candidate); + suggestion = data ? data.obj : null; + datasetName = data ? data.dataset : null; + id = $candidate ? $candidate.attr("id") : null; + this.input.trigger("cursorchange", id); + cancelMove = this._minLengthMet() && this.menu.update(query); + if (!cancelMove && !this.eventBus.before("cursorchange", suggestion, datasetName)) { + this.menu.setCursor($candidate); + if (data) { + if (typeof data.val === "string") { + this.input.setInputValue(data.val); + } + } else { + this.input.resetInputValue(); + this._updateHint(); + } + this.eventBus.trigger("cursorchange", suggestion, datasetName); + return true; + } + return false; + }, + destroy: function destroy() { + this.input.destroy(); + this.menu.destroy(); + } + }); + return Typeahead; + function c(ctx) { + var methods = [].slice.call(arguments, 1); + return function() { + var args = [].slice.call(arguments); + _.each(methods, function(method) { + return ctx[method].apply(ctx, args); + }); + }; + } + }(); + (function() { + "use strict"; + var old, keys, methods; + old = $.fn.typeahead; + keys = { + www: "tt-www", + attrs: "tt-attrs", + typeahead: "tt-typeahead" + }; + methods = { + initialize: function initialize(o, datasets) { + var www; + datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); + o = o || {}; + www = WWW(o.classNames); + return this.each(attach); + function attach() { + var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, status, typeahead, MenuConstructor; + _.each(datasets, function(d) { + d.highlight = !!o.highlight; + }); + $input = $(this); + $wrapper = $(www.html.wrapper); + $hint = $elOrNull(o.hint); + $menu = $elOrNull(o.menu); + defaultHint = o.hint !== false && !$hint; + defaultMenu = o.menu !== false && !$menu; + defaultHint && ($hint = buildHintFromInput($input, www)); + defaultMenu && ($menu = $(www.html.menu).css(www.css.menu)); + $hint && $hint.val(""); + $input = prepInput($input, www); + if (defaultHint || defaultMenu) { + $wrapper.css(www.css.wrapper); + $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint); + $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null); + } + MenuConstructor = defaultMenu ? DefaultMenu : Menu; + eventBus = new EventBus({ + el: $input + }); + input = new Input({ + hint: $hint, + input: $input, + menu: $menu + }, www); + menu = new MenuConstructor({ + node: $menu, + datasets: datasets + }, www); + status = new Status({ + $input: $input, + menu: menu + }); + typeahead = new Typeahead({ + input: input, + menu: menu, + eventBus: eventBus, + minLength: o.minLength, + autoselect: o.autoselect + }, www); + $input.data(keys.www, www); + $input.data(keys.typeahead, typeahead); + } + }, + isEnabled: function isEnabled() { + var enabled; + ttEach(this.first(), function(t) { + enabled = t.isEnabled(); + }); + return enabled; + }, + enable: function enable() { + ttEach(this, function(t) { + t.enable(); + }); + return this; + }, + disable: function disable() { + ttEach(this, function(t) { + t.disable(); + }); + return this; + }, + isActive: function isActive() { + var active; + ttEach(this.first(), function(t) { + active = t.isActive(); + }); + return active; + }, + activate: function activate() { + ttEach(this, function(t) { + t.activate(); + }); + return this; + }, + deactivate: function deactivate() { + ttEach(this, function(t) { + t.deactivate(); + }); + return this; + }, + isOpen: function isOpen() { + var open; + ttEach(this.first(), function(t) { + open = t.isOpen(); + }); + return open; + }, + open: function open() { + ttEach(this, function(t) { + t.open(); + }); + return this; + }, + close: function close() { + ttEach(this, function(t) { + t.close(); + }); + return this; + }, + select: function select(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.select($el); + }); + return success; + }, + autocomplete: function autocomplete(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.autocomplete($el); + }); + return success; + }, + moveCursor: function moveCursoe(delta) { + var success = false; + ttEach(this.first(), function(t) { + success = t.moveCursor(delta); + }); + return success; + }, + val: function val(newVal) { + var query; + if (!arguments.length) { + ttEach(this.first(), function(t) { + query = t.getVal(); + }); + return query; + } else { + ttEach(this, function(t) { + t.setVal(_.toStr(newVal)); + }); + return this; + } + }, + destroy: function destroy() { + ttEach(this, function(typeahead, $input) { + revert($input); + typeahead.destroy(); + }); + return this; + } + }; + $.fn.typeahead = function(method) { + if (methods[method]) { + return methods[method].apply(this, [].slice.call(arguments, 1)); + } else { + return methods.initialize.apply(this, arguments); + } + }; + $.fn.typeahead.noConflict = function noConflict() { + $.fn.typeahead = old; + return this; + }; + function ttEach($els, fn) { + $els.each(function() { + var $input = $(this), typeahead; + (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input); + }); + } + function buildHintFromInput($input, www) { + return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop({ + readonly: true, + required: false + }).removeAttr("id name placeholder").removeClass("required").attr({ + spellcheck: "false", + tabindex: -1 + }); + } + function prepInput($input, www) { + $input.data(keys.attrs, { + dir: $input.attr("dir"), + autocomplete: $input.attr("autocomplete"), + spellcheck: $input.attr("spellcheck"), + style: $input.attr("style") + }); + $input.addClass(www.classes.input).attr({ + spellcheck: false + }); + try { + !$input.attr("dir") && $input.attr("dir", "auto"); + } catch (e) {} + return $input; + } + function getBackgroundStyles($el) { + return { + backgroundAttachment: $el.css("background-attachment"), + backgroundClip: $el.css("background-clip"), + backgroundColor: $el.css("background-color"), + backgroundImage: $el.css("background-image"), + backgroundOrigin: $el.css("background-origin"), + backgroundPosition: $el.css("background-position"), + backgroundRepeat: $el.css("background-repeat"), + backgroundSize: $el.css("background-size") + }; + } + function revert($input) { + var www, $wrapper; + www = $input.data(keys.www); + $wrapper = $input.parent().filter(www.selectors.wrapper); + _.each($input.data(keys.attrs), function(val, key) { + _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); + }); + $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input); + if ($wrapper.length) { + $input.detach().insertAfter($wrapper); + $wrapper.remove(); + } + } + function $elOrNull(obj) { + var isValid, $el; + isValid = _.isJQuery(obj) || _.isElement(obj); + $el = isValid ? $(obj).first() : []; + return $el.length ? $el : null; + } + })(); +}); \ No newline at end of file diff --git a/docs/1.9.0/AsyncHTTPClient/search.json b/docs/1.9.0/AsyncHTTPClient/search.json new file mode 100644 index 000000000..2457c07fe --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/search.json @@ -0,0 +1 @@ +{"Structs/HTTPClientError.html#/s:s23CustomStringConvertibleP11descriptionSSvp":{"name":"description","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV10invalidURLACvpZ":{"name":"invalidURL","abstract":"

URL provided is invalid.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV9emptyHostACvpZ":{"name":"emptyHost","abstract":"

URL does not contain host.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV17missingSocketPathACvpZ":{"name":"missingSocketPath","abstract":"

URL does not contain a socketPath as a host for http(s)+unix shemes.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV15alreadyShutdownACvpZ":{"name":"alreadyShutdown","abstract":"

Client is shutdown and cannot be used for new requests.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV11emptySchemeACvpZ":{"name":"emptyScheme","abstract":"

URL does not contain scheme.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV17unsupportedSchemeyACSSFZ":{"name":"unsupportedScheme(_:)","abstract":"

Provided URL scheme is not supported, supported schemes are: http and https

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV11readTimeoutACvpZ":{"name":"readTimeout","abstract":"

Request timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV22remoteConnectionClosedACvpZ":{"name":"remoteConnectionClosed","abstract":"

Remote connection was closed unexpectedly.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV9cancelledACvpZ":{"name":"cancelled","abstract":"

Request was cancelled.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV32identityCodingIncorrectlyPresentACvpZ":{"name":"identityCodingIncorrectlyPresent","abstract":"

Request contains invalid identity encoding.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV29chunkedSpecifiedMultipleTimesACvpZ":{"name":"chunkedSpecifiedMultipleTimes","abstract":"

Request contains multiple chunks definitions.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20invalidProxyResponseACvpZ":{"name":"invalidProxyResponse","abstract":"

Proxy response was invalid.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20contentLengthMissingACvpZ":{"name":"contentLengthMissing","abstract":"

Request does not contain Content-Length header.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV27proxyAuthenticationRequiredACvpZ":{"name":"proxyAuthenticationRequired","abstract":"

Proxy Authentication Required.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20redirectLimitReachedACvpZ":{"name":"redirectLimitReached","abstract":"

Redirect Limit reached.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV21redirectCycleDetectedACvpZ":{"name":"redirectCycleDetected","abstract":"

Redirect Cycle detected.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV15uncleanShutdownACvpZ":{"name":"uncleanShutdown","abstract":"

Unclean shutdown.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20traceRequestWithBodyACvpZ":{"name":"traceRequestWithBody","abstract":"

A body was sent in a request with method TRACE.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV23invalidHeaderFieldNamesyACSaySSGFZ":{"name":"invalidHeaderFieldNames(_:)","abstract":"

Header field names contain invalid characters.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV18bodyLengthMismatchACvpZ":{"name":"bodyLengthMismatch","abstract":"

Body length is not equal to Content-Length.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV21writeAfterRequestSentACvpZ":{"name":"writeAfterRequestSent","abstract":"

Body part was written after request was fully sent.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV19incompatibleHeadersACvpZ":{"name":"incompatibleHeaders","abstract":"

Incompatible headers specified, for example Transfer-Encoding and Content-Length.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV14connectTimeoutACvpZ":{"name":"connectTimeout","abstract":"

Creating a new tcp connection timed out

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV21socksHandshakeTimeoutACvpZ":{"name":"socksHandshakeTimeout","abstract":"

The socks handshake timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV25httpProxyHandshakeTimeoutACvpZ":{"name":"httpProxyHandshakeTimeout","abstract":"

The http proxy connection creation timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV19tlsHandshakeTimeoutACvpZ":{"name":"tlsHandshakeTimeout","abstract":"

The tls handshake timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV43serverOfferedUnsupportedApplicationProtocolyACSSFZ":{"name":"serverOfferedUnsupportedApplicationProtocol(_:)","abstract":"

The remote server only offered an unsupported application protocol

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV16deadlineExceededACvpZ":{"name":"deadlineExceeded","abstract":"

The request deadline was exceeded. The request was cancelled because of this.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV22requestStreamCancelledACvpZ":{"name":"requestStreamCancelled","abstract":"

The remote server responded with a status code >= 300, before the full request was sent. The request stream","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV28getConnectionFromPoolTimeoutACvpZ":{"name":"getConnectionFromPoolTimeout","abstract":"

Aquiring a HTTP connection from the connection pool timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV31httpEndReceivedAfterHeadWith1xxACvpZ":{"name":"httpEndReceivedAfterHeadWith1xx","abstract":"

Undocumented

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html":{"name":"HTTPClientError","abstract":"

Possible client errors.

"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP0C0Qa":{"name":"Response","abstract":"

Undocumented

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didSendRequestHead4task_yAA0B0C4TaskCy_0C0QzG_8NIOHTTP1011HTTPRequestH0VtF":{"name":"didSendRequestHead(task:_:)","abstract":"

Called when the request head is sent. Will be called once.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didSendRequestPart4task_yAA0B0C4TaskCy_0C0QzG_7NIOCore6IODataOtF":{"name":"didSendRequestPart(task:_:)","abstract":"

Called when a part of the request body is sent. Could be called zero or more times.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didSendRequest4taskyAA0B0C4TaskCy_0C0QzG_tF":{"name":"didSendRequest(task:)","abstract":"

Called when the request is fully sent. Will be called once.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","abstract":"

Called when response head is received. Will be called once.","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","abstract":"

Called when part of a response body is received. Could be called zero or more times.","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","abstract":"

Called when error was thrown during request execution. Will be called zero or one time only. Request processing will be stopped after that.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","abstract":"

Called when the complete HTTP request is finished. You must return an instance of your Response associated type. Will be called once, except if an error occurred.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html":{"name":"HTTPClientResponseDelegate","abstract":"

HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed."},"Extensions/URL.html#/s:10Foundation3URLV15AsyncHTTPClientE21httpURLWithSocketPath3uriACSgSS_SStcfc":{"name":"init(httpURLWithSocketPath:uri:)","abstract":"

Initializes a newly created HTTP URL connecting to a unix domain socket path. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “http+unix” scheme.

","parent_name":"URL"},"Extensions/URL.html#/s:10Foundation3URLV15AsyncHTTPClientE22httpsURLWithSocketPath3uriACSgSS_SStcfc":{"name":"init(httpsURLWithSocketPath:uri:)","abstract":"

Initializes a newly created HTTPS URL connecting to a unix domain socket path over TLS. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “https+unix” scheme.

","parent_name":"URL"},"Extensions/URL.html":{"name":"URL"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B15CopyingDelegateC8Responsea":{"name":"Response","abstract":"

Undocumented

","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B15CopyingDelegateC12chunkHandlerAC7NIOCore15EventLoopFutureCyytGAE10ByteBufferVc_tcfc":{"name":"init(chunkHandler:)","abstract":"

Undocumented

","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"HTTPClientCopyingDelegate"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient19ResponseAccumulatorC0C0a":{"name":"Response","abstract":"

Undocumented

","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient19ResponseAccumulatorC7requestAcA0B0C7RequestV_tcfc":{"name":"init(request:)","abstract":"

Undocumented

","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"ResponseAccumulator"},"Classes/HTTPClient/NWTLSError.html#/status":{"name":"status","abstract":"

TLS error status. List of TLS errors can be found in

","parent_name":"NWTLSError"},"Classes/HTTPClient/NWTLSError.html#/init(_:reason:)":{"name":"init(_:reason:)","abstract":"

initialise a NWTLSError

","parent_name":"NWTLSError"},"Classes/HTTPClient/NWTLSError.html#/description":{"name":"description","parent_name":"NWTLSError"},"Classes/HTTPClient/NWPOSIXError.html#/errorCode":{"name":"errorCode","abstract":"

POSIX error code (enum)

","parent_name":"NWPOSIXError"},"Classes/HTTPClient/NWPOSIXError.html#/init(_:reason:)":{"name":"init(_:reason:)","abstract":"

Initialise a NWPOSIXError

","parent_name":"NWPOSIXError"},"Classes/HTTPClient/NWPOSIXError.html#/description":{"name":"description","parent_name":"NWPOSIXError"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC9eventLoop7NIOCore05EventE0_pvp":{"name":"eventLoop","abstract":"

The EventLoop the delegate will be executed on.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC12futureResult7NIOCore15EventLoopFutureCyxGvp":{"name":"futureResult","abstract":"

EventLoopFuture for the response returned by this request.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC4waitxyKF":{"name":"wait()","abstract":"

Waits for execution of this request to complete.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC6cancelyyF":{"name":"cancel()","abstract":"

Cancels the request execution.

","parent_name":"Task"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV5basic8username8passwordAESS_SStFZ":{"name":"basic(username:password:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV5basic11credentialsAESS_tFZ":{"name":"basic(credentials:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV6bearer6tokensAESS_tFZ":{"name":"bearer(tokens:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV11headerValueSSvp":{"name":"headerValue","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6method8NIOHTTP110HTTPMethodOvp":{"name":"method","abstract":"

Request HTTP method, defaults to GET.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url10Foundation3URLVvp":{"name":"url","abstract":"

Remote URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6schemeSSvp":{"name":"scheme","abstract":"

Remote HTTP scheme, resolved from URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV7headers8NIOHTTP111HTTPHeadersVvp":{"name":"headers","abstract":"

Request custom HTTP Headers, defaults to no headers.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4bodyAC4BodyVSgvp":{"name":"body","abstract":"

Request body, defaults to no body.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV16tlsConfiguration6NIOSSL16TLSConfigurationVSgvp":{"name":"tlsConfiguration","abstract":"

Request-specific TLS configuration, defaults to no request-specific TLS configuration.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4bodyAESS_8NIOHTTP110HTTPMethodOAJ11HTTPHeadersVAC4BodyVSgtKcfc":{"name":"init(url:method:headers:body:)","abstract":"

Create HTTP request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4body16tlsConfigurationAESS_8NIOHTTP110HTTPMethodOAK11HTTPHeadersVAC4BodyVSg6NIOSSL16TLSConfigurationVSgtKcfc":{"name":"init(url:method:headers:body:tlsConfiguration:)","abstract":"

Create HTTP request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4bodyAE10Foundation3URLV_8NIOHTTP110HTTPMethodOAM11HTTPHeadersVAC4BodyVSgtKcfc":{"name":"init(url:method:headers:body:)","abstract":"

Create an HTTP Request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4body16tlsConfigurationAE10Foundation3URLV_8NIOHTTP110HTTPMethodOAN11HTTPHeadersVAC4BodyVSg6NIOSSL16TLSConfigurationVSgtKcfc":{"name":"init(url:method:headers:body:tlsConfiguration:)","abstract":"

Create an HTTP Request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4hostSSvp":{"name":"host","abstract":"

Remote host, resolved from URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4portSivp":{"name":"port","abstract":"

Resolved port.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6useTLSSbvp":{"name":"useTLS","abstract":"

Whether request will be executed using secure socket.

","parent_name":"Request"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4hostSSvp":{"name":"host","abstract":"

Remote host of the request.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV6status8NIOHTTP118HTTPResponseStatusOvp":{"name":"status","abstract":"

Response HTTP status.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7version8NIOHTTP111HTTPVersionVvp":{"name":"version","abstract":"

Response HTTP version.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7headers8NIOHTTP111HTTPHeadersVvp":{"name":"headers","abstract":"

Reponse HTTP headers.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4body7NIOCore10ByteBufferVSgvp":{"name":"body","abstract":"

Response body.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4host6status7headers4bodyAESS_8NIOHTTP118HTTPResponseStatusOAJ11HTTPHeadersV7NIOCore10ByteBufferVSgtcfc":{"name":"init(host:status:headers:body:)","abstract":"

Create HTTP Response.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4host6status7version7headers4bodyAESS_8NIOHTTP118HTTPResponseStatusOAK11HTTPVersionVAK11HTTPHeadersV7NIOCore10ByteBufferVSgtcfc":{"name":"init(host:status:version:headers:body:)","abstract":"

Create HTTP Response.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7cookiesSayAC6CookieVGvp":{"name":"cookies","abstract":"

List of HTTP cookies returned by the server.

","parent_name":"Response"},"Classes/HTTPClient/Body/StreamWriter.html#/s:15AsyncHTTPClient0B0C4BodyV12StreamWriterV7closureAG7NIOCore15EventLoopFutureCyytGAI6IODataOc_tcfc":{"name":"init(closure:)","abstract":"

Create new StreamWriter

","parent_name":"StreamWriter"},"Classes/HTTPClient/Body/StreamWriter.html#/s:15AsyncHTTPClient0B0C4BodyV12StreamWriterV5writey7NIOCore15EventLoopFutureCyytGAI6IODataOF":{"name":"write(_:)","abstract":"

Write data to server.

","parent_name":"StreamWriter"},"Classes/HTTPClient/Body/StreamWriter.html":{"name":"StreamWriter","abstract":"

Chunk provider.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6lengthSiSgvp":{"name":"length","abstract":"

Body size. if nil,Transfer-Encoding will automatically be set to chunked. Otherwise a Content-Length","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6streamy7NIOCore15EventLoopFutureCyytGAE12StreamWriterVcvp":{"name":"stream","abstract":"

Body chunk provider.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV10byteBufferyAE7NIOCore04ByteE0VFZ":{"name":"byteBuffer(_:)","abstract":"

Create and stream body using ByteBuffer.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6stream6length_AESiSg_7NIOCore15EventLoopFutureCyytGAE12StreamWriterVctFZ":{"name":"stream(length:_:)","abstract":"

Create and stream body using StreamWriter.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV5bytesyAExSkRzs5UInt8V7ElementRtzlFZ":{"name":"bytes(_:)","abstract":"

Create and stream body using a collection of bytes.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6stringyAESSFZ":{"name":"string(_:)","abstract":"

Create and stream body using String.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV4datayAE10Foundation4DataVFZ":{"name":"data(_:)","abstract":"

Create and stream body using Data.

","parent_name":"Body"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4nameSSvp":{"name":"name","abstract":"

The name of the cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV5valueSSvp":{"name":"value","abstract":"

The cookie’s string value.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4pathSSvp":{"name":"path","abstract":"

The cookie’s path.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6domainSSSgvp":{"name":"domain","abstract":"

The domain of the cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6maxAgeSiSgvp":{"name":"maxAge","abstract":"

The cookie’s age in seconds.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV8httpOnlySbvp":{"name":"httpOnly","abstract":"

Whether the cookie should only be sent to HTTP servers.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6secureSbvp":{"name":"secure","abstract":"

Whether the cookie should only be sent over secure channels.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6header13defaultDomainAESgSS_SStcfc":{"name":"init(header:defaultDomain:)","abstract":"

Create a Cookie by parsing a Set-Cookie header.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV7expires10Foundation4DateVSgvp":{"name":"expires","abstract":"

The cookie’s expiration date.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4name5value4path6domain7expires6maxAge8httpOnly6secureAESS_S3SSg10Foundation4DateVSgSiSgS2btcfc":{"name":"init(name:value:path:domain:expires:maxAge:httpOnly:secure:)","abstract":"

Create HTTP cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Decompression.html#/s:15AsyncHTTPClient0B0C13DecompressionO8disabledyA2EmF":{"name":"disabled","abstract":"

Decompression is disabled.

","parent_name":"Decompression"},"Classes/HTTPClient/Decompression.html#/s:15AsyncHTTPClient0B0C13DecompressionO7enabledyAE18NIOHTTPCompression20NIOHTTPDecompressionO0C5LimitV_tcAEmF":{"name":"enabled(limit:)","abstract":"

Decompression is enabled.

","parent_name":"Decompression"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV11indifferentAEvpZ":{"name":"indifferent","abstract":"

Event Loop will be selected by the library.

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV8delegate2onAE7NIOCore0cD0_p_tFZ":{"name":"delegate(on:)","abstract":"

The delegate will be run on the specified EventLoop (and the Channel if possible).

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV18delegateAndChannel2onAE7NIOCore0cD0_p_tFZ":{"name":"delegateAndChannel(on:)","abstract":"

The delegate and the Channel will be run on the specified EventLoop.

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:s23CustomStringConvertibleP11descriptionSSvp":{"name":"description","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopGroupProvider.html#/s:15AsyncHTTPClient0B0C22EventLoopGroupProviderO6sharedyAE7NIOCore0cdE0_pcAEmF":{"name":"shared(_:)","abstract":"

EventLoopGroup will be provided by the user. Owner of this group is responsible for its lifecycle.

","parent_name":"EventLoopGroupProvider"},"Classes/HTTPClient/EventLoopGroupProvider.html#/s:15AsyncHTTPClient0B0C22EventLoopGroupProviderO9createNewyA2EmF":{"name":"createNew","abstract":"

EventLoopGroup will be created by the client. When syncShutdown is called, created EventLoopGroup will be shut down as well.

","parent_name":"EventLoopGroupProvider"},"Classes/HTTPClient/Configuration/HTTPVersion.html#/s:15AsyncHTTPClient0B0C13ConfigurationV11HTTPVersionV9http1OnlyAGvpZ":{"name":"http1Only","abstract":"

we only use HTTP/1, even if the server would supports HTTP/2

","parent_name":"HTTPVersion"},"Classes/HTTPClient/Configuration/HTTPVersion.html#/s:15AsyncHTTPClient0B0C13ConfigurationV11HTTPVersionV9automaticAGvpZ":{"name":"automatic","abstract":"

HTTP/2 is used if we connect to a server with HTTPS and the server supports HTTP/2, otherwise we use HTTP/1

","parent_name":"HTTPVersion"},"Classes/HTTPClient/Configuration/ConnectionPool.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14ConnectionPoolV11idleTimeout7NIOCore10TimeAmountVvp":{"name":"idleTimeout","abstract":"

Specifies amount of time connections are kept idle in the pool. After this time has passed without a new","parent_name":"ConnectionPool"},"Classes/HTTPClient/Configuration/ConnectionPool.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14ConnectionPoolV42concurrentHTTP1ConnectionsPerHostSoftLimitSivp":{"name":"concurrentHTTP1ConnectionsPerHostSoftLimit","abstract":"

The maximum number of connections that are kept alive in the connection pool per host. If requests with","parent_name":"ConnectionPool"},"Classes/HTTPClient/Configuration/ConnectionPool.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14ConnectionPoolV11idleTimeoutAG7NIOCore10TimeAmountV_tcfc":{"name":"init(idleTimeout:)","abstract":"

Undocumented

","parent_name":"ConnectionPool"},"Classes/HTTPClient/Configuration/ConnectionPool.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14ConnectionPoolV11idleTimeout42concurrentHTTP1ConnectionsPerHostSoftLimitAG7NIOCore10TimeAmountV_Sitcfc":{"name":"init(idleTimeout:concurrentHTTP1ConnectionsPerHostSoftLimit:)","abstract":"

Undocumented

","parent_name":"ConnectionPool"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08RedirectC0V8disallowAGvpZ":{"name":"disallow","abstract":"

Redirects are not followed.

","parent_name":"RedirectConfiguration"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08RedirectC0V6follow3max11allowCyclesAGSi_SbtFZ":{"name":"follow(max:allowCycles:)","abstract":"

Redirects are followed with a specified limit.

","parent_name":"RedirectConfiguration"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV7connect7NIOCore10TimeAmountVSgvp":{"name":"connect","abstract":"

Specifies connect timeout. If no connect timeout is given, a default 30 seconds timeout will applied.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV4read7NIOCore10TimeAmountVSgvp":{"name":"read","abstract":"

Specifies read timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV7connect4readAG7NIOCore10TimeAmountVSg_AMtcfc":{"name":"init(connect:read:)","abstract":"

Create timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV4hostSSvp":{"name":"host","abstract":"

Specifies Proxy server host.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV4portSivp":{"name":"port","abstract":"

Specifies Proxy server port.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV13authorizationAC13AuthorizationVSgvp":{"name":"authorization","abstract":"

Specifies Proxy server authorization.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV6server4host4portAGSS_SitFZ":{"name":"server(host:port:)","abstract":"

Create a HTTP proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV6server4host4port13authorizationAGSS_SiAC13AuthorizationVSgtFZ":{"name":"server(host:port:authorization:)","abstract":"

Create a HTTP proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV11socksServer4host4portAGSS_SitFZ":{"name":"socksServer(host:port:)","abstract":"

Create a SOCKSv5 proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC06NIOSSL16TLSConfigurationVSgvp":{"name":"tlsConfiguration","abstract":"

TLS configuration, defaults to TLSConfiguration.makeClientConfiguration().

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08redirectC0AE08RedirectC0Vvp":{"name":"redirectConfiguration","abstract":"

Enables following 3xx redirects automatically, defaults to RedirectConfiguration().

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7timeoutAE7TimeoutVvp":{"name":"timeout","abstract":"

Default client timeout, defaults to no read timeout and 10 seconds connect timeout.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14connectionPoolAE010ConnectionE0Vvp":{"name":"connectionPool","abstract":"

Connection pool configuration.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5proxyAE5ProxyVSgvp":{"name":"proxy","abstract":"

Upstream proxy, defaults to no proxy.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV13decompressionAC13DecompressionOvp":{"name":"decompression","abstract":"

Enables automatic body decompression. Supported algorithms are gzip and deflate.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV24ignoreUncleanSSLShutdownSbvp":{"name":"ignoreUncleanSSLShutdown","abstract":"

Ignore TLS unclean shutdown error, defaults to false.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV11httpVersionAE11HTTPVersionVvp":{"name":"httpVersion","abstract":"

is set to .automatic by default which will use HTTP/2 if run over https and the server supports it, otherwise HTTP/1

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC008redirectC07timeout14connectionPool5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL16TLSConfigurationVSg_AE08RedirectC0VSgAE7TimeoutVAE010ConnectionH0VAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(tlsConfiguration:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC008redirectC07timeout5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL16TLSConfigurationVSg_AE08RedirectC0VSgAE7TimeoutVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(tlsConfiguration:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout38maximumAllowedIdleTimeInConnectionPool5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutV7NIOCore0K6AmountVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:maximumAllowedIdleTimeInConnectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout14connectionPool5proxy24ignoreUncleanSSLShutdown13decompression24backgroundActivityLoggerAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutV7NIOCore10TimeAmountVAE5ProxyVSgSbAC13DecompressionO7Logging0Q0VSgtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:backgroundActivityLogger:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/Proxy.html":{"name":"Proxy","abstract":"

Proxy server configuration","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/Timeout.html":{"name":"Timeout","abstract":"

Timeout configuration.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html":{"name":"RedirectConfiguration","abstract":"

Specifies redirect processing settings.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/ConnectionPool.html":{"name":"ConnectionPool","abstract":"

Connection pool configuration.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/HTTPVersion.html":{"name":"HTTPVersion","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C14eventLoopGroup7NIOCore05EventdE0_pvp":{"name":"eventLoopGroup","abstract":"

Undocumented

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C22eventLoopGroupProvider13configurationA2C05EventdeF0O_AC13ConfigurationVtcfc":{"name":"init(eventLoopGroupProvider:configuration:)","abstract":"

Create an HTTPClient with specified EventLoopGroup provider and configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C22eventLoopGroupProvider13configuration24backgroundActivityLoggerA2C05EventdeF0O_AC13ConfigurationV7Logging0J0Vtcfc":{"name":"init(eventLoopGroupProvider:configuration:backgroundActivityLogger:)","abstract":"

Create an HTTPClient with specified EventLoopGroup provider and configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C12syncShutdownyyKF":{"name":"syncShutdown()","abstract":"

Shuts down the client and EventLoopGroup if it was created by the client.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C8shutdown5queue_y8Dispatch0E5QueueC_ys5Error_pSgctF":{"name":"shutdown(queue:_:)","abstract":"

Shuts down the client and event loop gracefully. This function is clearly an outlier in that it uses a completion","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3get3url8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AG11NIODeadlineVSgtF":{"name":"get(url:deadline:)","abstract":"

Execute GET request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3get3url8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"get(url:deadline:logger:)","abstract":"

Execute GET request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C4post3url4body8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"post(url:body:deadline:)","abstract":"

Execute POST request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C4post3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"post(url:body:deadline:logger:)","abstract":"

Execute POST request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C5patch3url4body8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"patch(url:body:deadline:)","abstract":"

Execute PATCH request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C5patch3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"patch(url:body:deadline:logger:)","abstract":"

Execute PATCH request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3put3url4body8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"put(url:body:deadline:)","abstract":"

Execute PUT request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3put3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"put(url:body:deadline:logger:)","abstract":"

Execute PUT request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C6delete3url8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AG11NIODeadlineVSgtF":{"name":"delete(url:deadline:)","abstract":"

Execute DELETE request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C6delete3url8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"delete(url:deadline:logger:)","abstract":"

Execute DELETE request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_SSAC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:url:body:deadline:logger:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_10socketPath03urlE04body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_S2SAC4BodyVSgAJ11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:socketPath:urlPath:body:deadline:logger:)","abstract":"

Execute arbitrary HTTP+UNIX request to a unix domain socket path, using the specified URL as the request to send to the server.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_16secureSocketPath03urlF04body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_S2SAC4BodyVSgAJ11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:secureSocketPath:urlPath:body:deadline:logger:)","abstract":"

Execute arbitrary HTTPS+UNIX request to a unix domain socket path over TLS, using the specified URL as the request to send to the server.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGAC7RequestV_AG11NIODeadlineVSgtF":{"name":"execute(request:deadline:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGAC7RequestV_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"execute(request:deadline:logger:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request9eventLoop8deadline7NIOCore05EventF6FutureCyAC8ResponseVGAC7RequestV_AC0iF10PreferenceVAH11NIODeadlineVSgtF":{"name":"execute(request:eventLoop:deadline:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request9eventLoop8deadline6logger7NIOCore05EventF6FutureCyAC8ResponseVGAC7RequestV_AC0jF10PreferenceVAI11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(request:eventLoop:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate8deadlineAC4TaskCy_8ResponseQzGAC7RequestV_x7NIOCore11NIODeadlineVSgtAA0bH8DelegateRzlF":{"name":"execute(request:delegate:deadline:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate8deadline6loggerAC4TaskCy_8ResponseQzGAC7RequestV_x7NIOCore11NIODeadlineVSg7Logging6LoggerVtAA0bI8DelegateRzlF":{"name":"execute(request:delegate:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate9eventLoop8deadlineAC4TaskCy_8ResponseQzGAC7RequestV_xAC05EventG10PreferenceV7NIOCore11NIODeadlineVSgtAA0bJ8DelegateRzlF":{"name":"execute(request:delegate:eventLoop:deadline:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate9eventLoop8deadline6loggerAC4TaskCy_8ResponseQzGAC7RequestV_xAC05EventG10PreferenceV7NIOCore11NIODeadlineVSg7Logging6LoggerVSgtAA0bK8DelegateRzlF":{"name":"execute(request:delegate:eventLoop:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Configuration.html":{"name":"Configuration","abstract":"

HTTPClient configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient/EventLoopGroupProvider.html":{"name":"EventLoopGroupProvider","abstract":"

Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

","parent_name":"HTTPClient"},"Classes/HTTPClient/EventLoopPreference.html":{"name":"EventLoopPreference","abstract":"

Specifies how the library will treat event loop passed by the user.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Decompression.html":{"name":"Decompression","abstract":"

Specifies decompression settings.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Cookie.html":{"name":"Cookie","abstract":"

A representation of an HTTP cookie.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Body.html":{"name":"Body","abstract":"

Represent request body.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Response.html":{"name":"Response","abstract":"

Represent HTTP response.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Request.html":{"name":"Request","abstract":"

Represent HTTP request.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Authorization.html":{"name":"Authorization","abstract":"

HTTP authentication

","parent_name":"HTTPClient"},"Classes/HTTPClient/Task.html":{"name":"Task","abstract":"

Response execution context. Will be created by the library and could be used for obtaining","parent_name":"HTTPClient"},"Classes/HTTPClient/NWPOSIXError.html":{"name":"NWPOSIXError","parent_name":"HTTPClient"},"Classes/HTTPClient/NWTLSError.html":{"name":"NWTLSError","parent_name":"HTTPClient"},"Classes/FileDownloadDelegate/Progress.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8ProgressV10totalBytesSiSgvp":{"name":"totalBytes","abstract":"

Undocumented

","parent_name":"Progress"},"Classes/FileDownloadDelegate/Progress.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8ProgressV13receivedBytesSivp":{"name":"receivedBytes","abstract":"

Undocumented

","parent_name":"Progress"},"Classes/FileDownloadDelegate/Progress.html":{"name":"Progress","abstract":"

The response type for this delegate: the total count of bytes as reported by the response","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8Responsea":{"name":"Response","abstract":"

Undocumented

","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient20FileDownloadDelegateC4path4pool10reportHead0H8ProgressACSS_8NIOPosix13NIOThreadPoolCy8NIOHTTP1012HTTPResponseI0VcSgyAC0J0VcSgtKcfc":{"name":"init(path:pool:reportHead:reportProgress:)","abstract":"

Initializes a new file download delegate.

","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html":{"name":"FileDownloadDelegate","abstract":"

Handles a streaming download to a given file path, allowing headers and progress to be reported.

"},"Classes/HTTPClient.html":{"name":"HTTPClient","abstract":"

HTTPClient class provides API for request execution.

"},"Classes/ResponseAccumulator.html":{"name":"ResponseAccumulator","abstract":"

Undocumented

"},"Classes/HTTPClientCopyingDelegate.html":{"name":"HTTPClientCopyingDelegate","abstract":"

Undocumented

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Extensions.html":{"name":"Extensions","abstract":"

The following extensions are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"},"Structs.html":{"name":"Structures","abstract":"

The following structures are available globally.

"}} \ No newline at end of file diff --git a/docs/1.9.0/AsyncHTTPClient/undocumented.json b/docs/1.9.0/AsyncHTTPClient/undocumented.json new file mode 100644 index 000000000..e37e866fd --- /dev/null +++ b/docs/1.9.0/AsyncHTTPClient/undocumented.json @@ -0,0 +1,201 @@ +{ + "warnings": [ + { + "file": "/code/Sources/AsyncHTTPClient/ConnectionPool/HTTPConnectionPool+Manager.swift", + "line": 128, + "symbol": "HTTPConnectionPool.Manager", + "symbol_kind": "source.lang.swift.decl.extension", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/ConnectionPool/HTTPRequestStateMachine.swift", + "line": 779, + "symbol": "HTTPRequestStateMachine.ResponseState", + "symbol_kind": "source.lang.swift.decl.extension", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP1StateMachine.swift", + "line": 617, + "symbol": "HTTPConnectionPool.HTTP1StateMachine", + "symbol_kind": "source.lang.swift.decl.extension", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+StateMachine.swift", + "line": 303, + "symbol": "HTTPConnectionPool.StateMachine", + "symbol_kind": "source.lang.swift.decl.extension", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 24, + "symbol": "FileDownloadDelegate.Progress.totalBytes", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 25, + "symbol": "FileDownloadDelegate.Progress.receivedBytes", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 30, + "symbol": "FileDownloadDelegate.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 67, + "symbol": "HTTPClient.eventLoopGroup", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 658, + "symbol": "HTTPClient.Configuration.init(tlsConfiguration:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 676, + "symbol": "HTTPClient.Configuration.init(tlsConfiguration:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 693, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:maximumAllowedIdleTimeInConnectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 711, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:backgroundActivityLogger:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 730, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 878, + "symbol": "HTTPClient.Configuration.ConnectionPool.init(idleTimeout:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 882, + "symbol": "HTTPClient.Configuration.ConnectionPool.init(idleTimeout:concurrentHTTP1ConnectionsPerHostSoftLimit:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 888, + "symbol": "HTTPClient.Configuration.HTTPVersion", + "symbol_kind": "source.lang.swift.decl.struct", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 1026, + "symbol": "HTTPClientError.httpEndReceivedAfterHeadWith1xx", + "symbol_kind": "source.lang.swift.decl.var.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 288, + "symbol": "HTTPClient.Authorization.basic(username:password:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 292, + "symbol": "HTTPClient.Authorization.basic(credentials:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 296, + "symbol": "HTTPClient.Authorization.bearer(tokens:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 300, + "symbol": "HTTPClient.Authorization.headerValue", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 311, + "symbol": "ResponseAccumulator", + "symbol_kind": "source.lang.swift.decl.class", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 312, + "symbol": "ResponseAccumulator.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 325, + "symbol": "ResponseAccumulator.init(request:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 418, + "symbol": "HTTPClientResponseDelegate.Response", + "symbol_kind": "source.lang.swift.decl.associatedtype", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 17, + "symbol": "HTTPClientCopyingDelegate", + "symbol_kind": "source.lang.swift.decl.class", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 18, + "symbol": "HTTPClientCopyingDelegate.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 22, + "symbol": "HTTPClientCopyingDelegate.init(chunkHandler:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + } + ], + "source_directory": "/code" +} \ No newline at end of file diff --git a/docs/1.9.1/AsyncHTTPClient/Classes.html b/docs/1.9.1/AsyncHTTPClient/Classes.html new file mode 100644 index 000000000..32d9b7134 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes.html @@ -0,0 +1,311 @@ + + + + Classes Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Classes

+

The following classes are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + FileDownloadDelegate + +
    +
    +
    +
    +
    +
    +

    Handles a streaming download to a given file path, allowing headers and progress to be reported.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class FileDownloadDelegate : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + HTTPClient + +
    +
    +
    +
    +
    +
    +

    HTTPClient class provides API for request execution.

    + +

    Example:

    +
        let client = HTTPClient(eventLoopGroupProvider: .createNew)
    +    client.get(url: "https://swift.org/", deadline: .now() + .seconds(1)).whenComplete { result in
    +        switch result {
    +        case .failure(let error):
    +            // process error
    +        case .success(let response):
    +            if let response.status == .ok {
    +                // handle response
    +            } else {
    +                // handle remote error
    +            }
    +        }
    +    }
    +
    + +

    It is important to close the client instance, for example in a defer statement, after use to cleanly shutdown the underlying NIO EventLoopGroup:

    +
        try client.syncShutdown()
    +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class HTTPClient
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + ResponseAccumulator + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class ResponseAccumulator : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Undocumented

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class HTTPClientCopyingDelegate : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/FileDownloadDelegate.html b/docs/1.9.1/AsyncHTTPClient/Classes/FileDownloadDelegate.html new file mode 100644 index 000000000..3b677d651 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/FileDownloadDelegate.html @@ -0,0 +1,454 @@ + + + + FileDownloadDelegate Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

FileDownloadDelegate

+
+
+ +
public final class FileDownloadDelegate : HTTPClientResponseDelegate
+ +
+
+

Handles a streaming download to a given file path, allowing headers and progress to be reported.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + Progress + +
    +
    +
    +
    +
    +
    +

    The response type for this delegate: the total count of bytes as reported by the response +“Content-Length” header (if available) and the count of bytes downloaded.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Progress
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public typealias Response = Progress
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Initializes a new file download delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(
    +    path: String,
    +    pool: NIOThreadPool = NIOThreadPool(numberOfThreads: 1),
    +    reportHead: ((HTTPResponseHead) -> Void)? = nil,
    +    reportProgress: ((Progress) -> Void)? = nil
    +) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + path + + +
    +

    Path to a file you’d like to write the download to.

    +
    +
    + + pool + + +
    +

    A thread pool to use for asynchronous file I/O.

    +
    +
    + + reportHead + + +
    +

    A closure called when the response head is available.

    +
    +
    + + reportProgress + + +
    +

    A closure called when a body chunk has been downloaded, with +the total byte count and download byte count passed to it as arguments. The callbacks +will be invoked in the same threading context that the delegate itself is invoked, +as controlled by EventLoopPreference.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveHead(
    +    task: HTTPClient.Task<Response>,
    +    _ head: HTTPResponseHead
    +) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveBodyPart(
    +    task: HTTPClient.Task<Response>,
    +    _ buffer: ByteBuffer
    +) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveError(task: HTTPClient.Task<Progress>, _ error: Error)
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didFinishRequest(task: HTTPClient.Task<Response>) throws -> Response
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html b/docs/1.9.1/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html new file mode 100644 index 000000000..b630a6cb3 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html @@ -0,0 +1,238 @@ + + + + Progress Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Progress

+
+
+ +
public struct Progress
+ +
+
+

The response type for this delegate: the total count of bytes as reported by the response +“Content-Length” header (if available) and the count of bytes downloaded.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + totalBytes + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var totalBytes: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + receivedBytes + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var receivedBytes: Int
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient.html b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient.html new file mode 100644 index 000000000..84e00bc9c --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient.html @@ -0,0 +1,2483 @@ + + + + HTTPClient Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClient

+
+
+ +
public class HTTPClient
+ +
+
+

HTTPClient class provides API for request execution.

+ +

Example:

+
    let client = HTTPClient(eventLoopGroupProvider: .createNew)
+    client.get(url: "https://swift.org/", deadline: .now() + .seconds(1)).whenComplete { result in
+        switch result {
+        case .failure(let error):
+            // process error
+        case .success(let response):
+            if let response.status == .ok {
+                // handle response
+            } else {
+                // handle remote error
+            }
+        }
+    }
+
+ +

It is important to close the client instance, for example in a defer statement, after use to cleanly shutdown the underlying NIO EventLoopGroup:

+
    try client.syncShutdown()
+
+ + +
+
+ +
+
+
+
    +
  • +
    + + + + eventLoopGroup + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let eventLoopGroup: EventLoopGroup
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTPClient with specified EventLoopGroup provider and configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public convenience init(eventLoopGroupProvider: EventLoopGroupProvider,
    +                        configuration: Configuration = Configuration())
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + eventLoopGroupProvider + + +
    +

    Specify how EventLoopGroup will be created.

    +
    +
    + + configuration + + +
    +

    Client configuration.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTPClient with specified EventLoopGroup provider and configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public required init(eventLoopGroupProvider: EventLoopGroupProvider,
    +                     configuration: Configuration = Configuration(),
    +                     backgroundActivityLogger: Logger)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + eventLoopGroupProvider + + +
    +

    Specify how EventLoopGroup will be created.

    +
    +
    + + configuration + + +
    +

    Client configuration.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + syncShutdown() + +
    +
    +
    +
    +
    +
    +

    Shuts down the client and EventLoopGroup if it was created by the client.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func syncShutdown() throws
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + shutdown(queue:_:) + +
    +
    +
    +
    +
    +
    +

    Shuts down the client and event loop gracefully. This function is clearly an outlier in that it uses a completion +callback instead of an EventLoopFuture. The reason for that is that NIO’s EventLoopFutures will call back on an event loop. +The virtue of this function is to shut the event loop down. To work around that we call back on a DispatchQueue +instead.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func shutdown(queue: DispatchQueue = .global(), _ callback: @escaping (Error?) -> Void)
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + get(url:deadline:) + +
    +
    +
    +
    +
    +
    +

    Execute GET request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func get(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute GET request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func get(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute POST request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute POST request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PATCH request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PATCH request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PUT request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PUT request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + delete(url:deadline:) + +
    +
    +
    +
    +
    +
    +

    Execute DELETE request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func delete(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    The time when the request must have been completed by.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute DELETE request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func delete(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    The time when the request must have been completed by.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + url + + +
    +

    Request url.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP+UNIX request to a unix domain socket path, using the specified URL as the request to send to the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, socketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + urlPath + + +
    +

    The URL path and query that will be sent to the server.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTPS+UNIX request to a unix domain socket path over TLS, using the specified URL as the request to send to the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, secureSocketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + secureSocketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + urlPath + + +
    +

    The URL path and query that will be sent to the server.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, eventLoop: EventLoopPreference, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request,
    +                    eventLoop eventLoopPreference: EventLoopPreference,
    +                    deadline: NIODeadline? = nil,
    +                    logger: Logger?) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          deadline: NIODeadline? = nil) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          deadline: NIODeadline? = nil,
    +                                                          logger: Logger) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          eventLoop eventLoopPreference: EventLoopPreference,
    +                                                          deadline: NIODeadline? = nil) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(
    +    request: Request,
    +    delegate: Delegate,
    +    eventLoop eventLoopPreference: EventLoopPreference,
    +    deadline: NIODeadline? = nil,
    +    logger originalLogger: Logger?
    +) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + Configuration + +
    +
    +
    +
    +
    +
    +

    HTTPClient configuration.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Configuration
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum EventLoopGroupProvider
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + EventLoopPreference + +
    +
    +
    +
    +
    +
    +

    Specifies how the library will treat event loop passed by the user.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct EventLoopPreference
    +
    extension HTTPClient.EventLoopPreference: CustomStringConvertible
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Decompression + +
    +
    +
    +
    +
    +
    +

    Specifies decompression settings.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum Decompression
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Cookie + +
    +
    +
    +
    +
    +
    +

    A representation of an HTTP cookie.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Cookie
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Body + +
    +
    +
    +
    +
    +
    +

    Represent request body.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Body
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Represent HTTP response.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Response
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Request + +
    +
    +
    +
    +
    +
    +

    Represent HTTP request.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Request
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Authorization + +
    +
    +
    +
    +
    +
    +

    HTTP authentication

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Authorization : Hashable
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Task + +
    +
    +
    +
    +
    +
    +

    Response execution context. Will be created by the library and could be used for obtaining +EventLoopFuture<Response> of the execution or cancellation of the execution.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class Task<Response>
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + NWPOSIXError + +
    +
    +
    +
    +
    +
    + + See more +
    +
    +
    +
  • +
  • +
    + + + + NWTLSError + +
    +
    +
    +
    +
    +
    + + See more +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Authorization.html b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Authorization.html new file mode 100644 index 000000000..33e330f39 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Authorization.html @@ -0,0 +1,297 @@ + + + + Authorization Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Authorization

+
+
+ +
public struct Authorization : Hashable
+ +
+
+

HTTP authentication

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Body.html b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Body.html new file mode 100644 index 000000000..7e951d8eb --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Body.html @@ -0,0 +1,528 @@ + + + + Body Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Body

+
+
+ +
public struct Body
+ +
+
+

Represent request body.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + StreamWriter + +
    +
    +
    +
    +
    +
    +

    Chunk provider.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct StreamWriter
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + length + +
    +
    +
    +
    +
    +
    +

    Body size. if nil,Transfer-Encoding will automatically be set to chunked. Otherwise a Content-Length +header is set with the given length.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var length: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + stream + +
    +
    +
    +
    +
    +
    +

    Body chunk provider.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var stream: (StreamWriter) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + byteBuffer(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using ByteBuffer.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func byteBuffer(_ buffer: ByteBuffer) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + buffer + + +
    +

    Body ByteBuffer representation.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + stream(length:_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using StreamWriter.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func stream(length: Int? = nil, _ stream: @escaping (StreamWriter) -> EventLoopFuture<Void>) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + length + + +
    +

    Body size. If nil, Transfer-Encoding will automatically be set to chunked. Otherwise a Content-Length +header is set with the given length.

    +
    +
    + + stream + + +
    +

    Body chunk provider.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + bytes(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using a collection of bytes.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    @inlinable
    +public static func bytes<Bytes>(_ bytes: Bytes) -> Body where Bytes : RandomAccessCollection, Bytes.Element == UInt8
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    Body binary representation.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + string(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using String.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func string(_ string: String) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + string + + +
    +

    Body String representation.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + data(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using Data.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func data(_ data: Data) -> HTTPClient.Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + bytes + + +
    +

    Body Data representation.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html new file mode 100644 index 000000000..758192866 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html @@ -0,0 +1,275 @@ + + + + StreamWriter Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

StreamWriter

+
+
+ +
public struct StreamWriter
+ +
+
+

Chunk provider.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + init(closure:) + +
    +
    +
    +
    +
    +
    +

    Create new StreamWriter

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(closure: @escaping (IOData) -> EventLoopFuture<Void>)
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + closure + + +
    +

    function that will be called to write actual bytes to the channel.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + write(_:) + +
    +
    +
    +
    +
    +
    +

    Write data to server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func write(_ data: IOData) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    IOData to write.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Configuration.html b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Configuration.html new file mode 100644 index 000000000..e8ec740a0 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Configuration.html @@ -0,0 +1,775 @@ + + + + Configuration Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Configuration

+
+
+ +
public struct Configuration
+ +
+
+

HTTPClient configuration.

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html new file mode 100644 index 000000000..dfd8ce52e --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html @@ -0,0 +1,299 @@ + + + + ConnectionPool Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

ConnectionPool

+
+
+ +
public struct ConnectionPool : Hashable
+ +
+
+

Connection pool configuration.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + idleTimeout + +
    +
    +
    +
    +
    +
    +

    Specifies amount of time connections are kept idle in the pool. After this time has passed without a new +request the connections are closed.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var idleTimeout: TimeAmount
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The maximum number of connections that are kept alive in the connection pool per host. If requests with +an explicit eventLoopRequirement are sent, this number might be exceeded due to overflow connections.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var concurrentHTTP1ConnectionsPerHostSoftLimit: Int
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + init(idleTimeout:) + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(idleTimeout: TimeAmount = .seconds(60))
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(idleTimeout: TimeAmount, concurrentHTTP1ConnectionsPerHostSoftLimit: Int)
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/HTTPVersion.html b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/HTTPVersion.html new file mode 100644 index 000000000..0497f375f --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/HTTPVersion.html @@ -0,0 +1,237 @@ + + + + HTTPVersion Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPVersion

+
+
+ +
public struct HTTPVersion
+ +
+
+

Undocumented

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + http1Only + +
    +
    +
    +
    +
    +
    +

    we only use HTTP/1, even if the server would supports HTTP/2

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let http1Only: `Self`
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + automatic + +
    +
    +
    +
    +
    +
    +

    HTTP/2 is used if we connect to a server with HTTPS and the server supports HTTP/2, otherwise we use HTTP/1

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let automatic: `Self`
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html new file mode 100644 index 000000000..1c470625a --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html @@ -0,0 +1,475 @@ + + + + Proxy Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Proxy

+
+
+ +
public struct Proxy
+ +
+
+

Proxy server configuration +Specifies the remote address of an HTTP proxy.

+ +

Adding an Proxy to your client’s HTTPClient.Configuration +will cause requests to be passed through the specified proxy using the +HTTP CONNECT method.

+ +

If a TLSConfiguration is used in conjunction with HTTPClient.Configuration.Proxy, +TLS will be established after successful proxy, between your client +and the destination server.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server host.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + port + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server port.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var port: Int
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + authorization + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server authorization.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var authorization: HTTPClient.Authorization? { get set }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + server(host:port:) + +
    +
    +
    +
    +
    +
    +

    Create a HTTP proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func server(host: String, port: Int) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + host + + +
    +

    proxy server host.

    +
    +
    + + port + + +
    +

    proxy server port.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a HTTP proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func server(host: String, port: Int, authorization: HTTPClient.Authorization? = nil) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + host + + +
    +

    proxy server host.

    +
    +
    + + port + + +
    +

    proxy server port.

    +
    +
    + + authorization + + +
    +

    proxy server authorization.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a SOCKSv5 proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func socksServer(host: String, port: Int = 1080) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + host + + +
    +

    The SOCKSv5 proxy address.

    +
    +
    + + port + + +
    +

    The SOCKSv5 proxy port, defaults to 1080.

    +
    +
    +
    +
    +

    Return Value

    +

    A new instance of Proxy configured to connect to a SOCKSv5 server.

    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html new file mode 100644 index 000000000..a6fdd1deb --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html @@ -0,0 +1,273 @@ + + + + RedirectConfiguration Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

RedirectConfiguration

+
+
+ +
public struct RedirectConfiguration
+ +
+
+

Specifies redirect processing settings.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + disallow + +
    +
    +
    +
    +
    +
    +

    Redirects are not followed.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let disallow: HTTPClient.Configuration.RedirectConfiguration
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Redirects are followed with a specified limit.

    +
    +

    Warning

    +

    Cycle detection will keep all visited URLs in memory which means a malicious server could use this as a denial-of-service vector.

    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func follow(max: Int, allowCycles: Bool) -> RedirectConfiguration
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + max + + +
    +

    The maximum number of allowed redirects.

    +
    +
    + + allowCycles + + +
    +

    Whether cycles are allowed.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html new file mode 100644 index 000000000..4f2345d59 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html @@ -0,0 +1,299 @@ + + + + Timeout Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Timeout

+
+
+ +
public struct Timeout
+ +
+
+

Timeout configuration.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + connect + +
    +
    +
    +
    +
    +
    +

    Specifies connect timeout. If no connect timeout is given, a default 30 seconds timeout will applied.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var connect: TimeAmount?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + read + +
    +
    +
    +
    +
    +
    +

    Specifies read timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var read: TimeAmount?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + init(connect:read:) + +
    +
    +
    +
    +
    +
    +

    Create timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(connect: TimeAmount? = nil, read: TimeAmount? = nil)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + connect + + +
    +

    connect timeout. Will default to 10 seconds, if no value is +provided. See var connectionCreationTimeout

    +
    +
    + + read + + +
    +

    read timeout.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Cookie.html b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Cookie.html new file mode 100644 index 000000000..f081d24a5 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Cookie.html @@ -0,0 +1,615 @@ + + + + Cookie Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Cookie

+
+
+ +
public struct Cookie
+ +
+
+

A representation of an HTTP cookie.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + name + +
    +
    +
    +
    +
    +
    +

    The name of the cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var name: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + value + +
    +
    +
    +
    +
    +
    +

    The cookie’s string value.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var value: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + path + +
    +
    +
    +
    +
    +
    +

    The cookie’s path.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var path: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + domain + +
    +
    +
    +
    +
    +
    +

    The domain of the cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var domain: String?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + maxAge + +
    +
    +
    +
    +
    +
    +

    The cookie’s age in seconds.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var maxAge: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + httpOnly + +
    +
    +
    +
    +
    +
    +

    Whether the cookie should only be sent to HTTP servers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var httpOnly: Bool
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + secure + +
    +
    +
    +
    +
    +
    +

    Whether the cookie should only be sent over secure channels.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var secure: Bool
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a Cookie by parsing a Set-Cookie header.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(header: String, defaultDomain: String)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + header + + +
    +

    String representation of the Set-Cookie response header.

    +
    +
    + + defaultDomain + + +
    +

    Default domain to use if cookie was sent without one.

    +
    +
    +
    +
    +

    Return Value

    +

    nil if the header is invalid.

    +
    + +
    +
    +
  • +
  • +
    + + + + expires + +
    +
    +
    +
    +
    +
    +

    The cookie’s expiration date.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var expires: Date? { get set }
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(name: String, value: String, path: String = "/", domain: String? = nil, expires: Date? = nil, maxAge: Int? = nil, httpOnly: Bool = false, secure: Bool = false)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + name + + +
    +

    The name of the cookie.

    +
    +
    + + value + + +
    +

    The cookie’s string value.

    +
    +
    + + path + + +
    +

    The cookie’s path.

    +
    +
    + + domain + + +
    +

    The domain of the cookie, defaults to nil.

    +
    +
    + + expires + + +
    +

    The cookie’s expiration date, defaults to nil.

    +
    +
    + + maxAge + + +
    +

    The cookie’s age in seconds, defaults to nil.

    +
    +
    + + httpOnly + + +
    +

    Whether this cookie should be used by HTTP servers only, defaults to false.

    +
    +
    + + secure + + +
    +

    Whether this cookie should only be sent using secure channels, defaults to false.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Decompression.html b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Decompression.html new file mode 100644 index 000000000..f94a48938 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Decompression.html @@ -0,0 +1,237 @@ + + + + Decompression Enumeration Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Decompression

+
+
+ +
public enum Decompression
+ +
+
+

Specifies decompression settings.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + disabled + +
    +
    +
    +
    +
    +
    +

    Decompression is disabled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case disabled
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + enabled(limit:) + +
    +
    +
    +
    +
    +
    +

    Decompression is enabled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case enabled(limit: NIOHTTPDecompression.DecompressionLimit)
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html new file mode 100644 index 000000000..95d9602d4 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html @@ -0,0 +1,237 @@ + + + + EventLoopGroupProvider Enumeration Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

EventLoopGroupProvider

+
+
+ +
public enum EventLoopGroupProvider
+ +
+
+

Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + shared(_:) + +
    +
    +
    +
    +
    +
    +

    EventLoopGroup will be provided by the user. Owner of this group is responsible for its lifecycle.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case shared(EventLoopGroup)
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + createNew + +
    +
    +
    +
    +
    +
    +

    EventLoopGroup will be created by the client. When syncShutdown is called, created EventLoopGroup will be shut down as well.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case createNew
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html new file mode 100644 index 000000000..6967d9e45 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html @@ -0,0 +1,304 @@ + + + + EventLoopPreference Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

EventLoopPreference

+
+
+ +
public struct EventLoopPreference
+
extension HTTPClient.EventLoopPreference: CustomStringConvertible
+ +
+
+

Specifies how the library will treat event loop passed by the user.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + indifferent + +
    +
    +
    +
    +
    +
    +

    Event Loop will be selected by the library.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let indifferent: HTTPClient.EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + delegate(on:) + +
    +
    +
    +
    +
    +
    +

    The delegate will be run on the specified EventLoop (and the Channel if possible).

    + +

    This will call the configured delegate on eventLoop and will try to use a Channel on the same +EventLoop but will not establish a new network connection just to satisfy the EventLoop preference if +another existing connection on a different EventLoop is readily available from a connection pool.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func delegate(on eventLoop: EventLoop) -> EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The delegate and the Channel will be run on the specified EventLoop.

    + +

    Use this for use-cases where you prefer a new connection to be established over re-using an existing +connection that might be on a different EventLoop.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func delegateAndChannel(on eventLoop: EventLoop) -> EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var description: String { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/NWPOSIXError.html b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/NWPOSIXError.html new file mode 100644 index 000000000..e25e077f6 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/NWPOSIXError.html @@ -0,0 +1,222 @@ + + + + NWPOSIXError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

NWPOSIXError

+ +
+
+ +
+
+
+
    +
  • +
    + + + + errorCode + +
    +
    +
    +
    +
    +
    +

    POSIX error code (enum)

    + +
    +
    +
    +
  • +
  • +
    + + + + init(_:reason:) + +
    +
    +
    +
    +
    +
    +

    Initialise a NWPOSIXError

    + +
    +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/NWTLSError.html b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/NWTLSError.html new file mode 100644 index 000000000..478ee8639 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/NWTLSError.html @@ -0,0 +1,222 @@ + + + + NWTLSError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

NWTLSError

+ +
+
+ +
+
+
+
    +
  • +
    + + + + status + +
    +
    +
    +
    +
    +
    +

    TLS error status. List of TLS errors can be found in

    + +
    +
    +
    +
  • +
  • +
    + + + + init(_:reason:) + +
    +
    +
    +
    +
    +
    +

    initialise a NWTLSError

    + +
    +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Request.html b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Request.html new file mode 100644 index 000000000..0387a0f6b --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Request.html @@ -0,0 +1,875 @@ + + + + Request Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Request

+
+
+ +
public struct Request
+ +
+
+

Represent HTTP request.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + method + +
    +
    +
    +
    +
    +
    +

    Request HTTP method, defaults to GET.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let method: HTTPMethod
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + url + +
    +
    +
    +
    +
    +
    +

    Remote URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let url: URL
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + scheme + +
    +
    +
    +
    +
    +
    +

    Remote HTTP scheme, resolved from URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var scheme: String { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + headers + +
    +
    +
    +
    +
    +
    +

    Request custom HTTP Headers, defaults to no headers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var headers: HTTPHeaders
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + body + +
    +
    +
    +
    +
    +
    +

    Request body, defaults to no body.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var body: Body?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + tlsConfiguration + +
    +
    +
    +
    +
    +
    +

    Request-specific TLS configuration, defaults to no request-specific TLS configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var tlsConfiguration: TLSConfiguration?
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP request.

    +
    +

    Throws

    +
      +
    • invalidURL if URL cannot be parsed.
    • +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: String, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + version + + +
    +

    HTTP version.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP request.

    +
    +

    Throws

    +
      +
    • invalidURL if URL cannot be parsed.
    • +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: String, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil, tlsConfiguration: TLSConfiguration?) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + version + + +
    +

    HTTP version.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + tlsConfiguration + + +
    +

    Request TLS configuration

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTP Request.

    +
    +

    Throws

    +
      +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    • missingSocketPath if URL does not contains a socketPath as an encoded host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTP Request.

    +
    +

    Throws

    +
      +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    • missingSocketPath if URL does not contains a socketPath as an encoded host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil, tlsConfiguration: TLSConfiguration?) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + tlsConfiguration + + +
    +

    Request TLS configuration

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Remote host, resolved from URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + port + +
    +
    +
    +
    +
    +
    +

    Resolved port.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var port: Int { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + useTLS + +
    +
    +
    +
    +
    +
    +

    Whether request will be executed using secure socket.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var useTLS: Bool { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Response.html b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Response.html new file mode 100644 index 000000000..8407184f4 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Response.html @@ -0,0 +1,540 @@ + + + + Response Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Response

+
+
+ +
public struct Response
+ +
+
+

Represent HTTP response.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Remote host of the request.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + status + +
    +
    +
    +
    +
    +
    +

    Response HTTP status.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var status: HTTPResponseStatus
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + version + +
    +
    +
    +
    +
    +
    +

    Response HTTP version.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var version: HTTPVersion
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + headers + +
    +
    +
    +
    +
    +
    +

    Reponse HTTP headers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var headers: HTTPHeaders
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + body + +
    +
    +
    +
    +
    +
    +

    Response body.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var body: ByteBuffer?
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP Response.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    @available(*, deprecated, renamed: "init(host:status:version:headers:body:﹚")
    +public init(host: String, status: HTTPResponseStatus, headers: HTTPHeaders, body: ByteBuffer?)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + host + + +
    +

    Remote host of the request.

    +
    +
    + + status + + +
    +

    Response HTTP status.

    +
    +
    + + headers + + +
    +

    Reponse HTTP headers.

    +
    +
    + + body + + +
    +

    Response body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP Response.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(host: String, status: HTTPResponseStatus, version: HTTPVersion, headers: HTTPHeaders, body: ByteBuffer?)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + host + + +
    +

    Remote host of the request.

    +
    +
    + + status + + +
    +

    Response HTTP status.

    +
    +
    + + version + + +
    +

    Response HTTP version.

    +
    +
    + + headers + + +
    +

    Reponse HTTP headers.

    +
    +
    + + body + + +
    +

    Response body.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + cookies + +
    +
    +
    +
    +
    +
    +

    List of HTTP cookies returned by the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var cookies: [HTTPClient.Cookie] { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Task.html b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Task.html new file mode 100644 index 000000000..19874a6d5 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClient/Task.html @@ -0,0 +1,307 @@ + + + + Task Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Task

+
+
+ +
public final class Task<Response>
+ +
+
+

Response execution context. Will be created by the library and could be used for obtaining +EventLoopFuture<Response> of the execution or cancellation of the execution.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + eventLoop + +
    +
    +
    +
    +
    +
    +

    The EventLoop the delegate will be executed on.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let eventLoop: EventLoop
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + futureResult + +
    +
    +
    +
    +
    +
    +

    EventLoopFuture for the response returned by this request.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var futureResult: EventLoopFuture<Response> { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + wait() + +
    +
    +
    +
    +
    +
    +

    Waits for execution of this request to complete.

    +
    +

    Throws

    + The error value of the EventLoopFuture if it errors. + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func wait() throws -> Response
    + +
    +
    +
    +

    Return Value

    +

    The value of the EventLoopFuture when it completes.

    +
    + +
    +
    +
  • +
  • +
    + + + + cancel() + +
    +
    +
    +
    +
    +
    +

    Cancels the request execution.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func cancel()
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html new file mode 100644 index 000000000..a6fc343f6 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html @@ -0,0 +1,295 @@ + + + + HTTPClientCopyingDelegate Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientCopyingDelegate

+
+
+ +
public final class HTTPClientCopyingDelegate : HTTPClientResponseDelegate
+ +
+
+

Undocumented

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Classes/ResponseAccumulator.html b/docs/1.9.1/AsyncHTTPClient/Classes/ResponseAccumulator.html new file mode 100644 index 000000000..4349f311a --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Classes/ResponseAccumulator.html @@ -0,0 +1,353 @@ + + + + ResponseAccumulator Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

ResponseAccumulator

+
+
+ +
public class ResponseAccumulator : HTTPClientResponseDelegate
+ +
+
+

Undocumented

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Extensions.html b/docs/1.9.1/AsyncHTTPClient/Extensions.html new file mode 100644 index 000000000..394549bb6 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Extensions.html @@ -0,0 +1,194 @@ + + + + Extensions Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Extensions

+

The following extensions are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + URL + +
    +
    +
    +
    +
    +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    extension URL
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Extensions/URL.html b/docs/1.9.1/AsyncHTTPClient/Extensions/URL.html new file mode 100644 index 000000000..8929a8e97 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Extensions/URL.html @@ -0,0 +1,295 @@ + + + + URL Extension Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

URL

+
+
+ +
extension URL
+ +
+
+ +
+
+ +
+
+
+
    +
  • + +
    +
    +
    +
    +
    +

    Initializes a newly created HTTP URL connecting to a unix domain socket path. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “http+unix” scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(httpURLWithSocketPath socketPath: String, uri: String = "/")
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + uri + + +
    +

    The URI path and query that will be sent to the server.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Initializes a newly created HTTPS URL connecting to a unix domain socket path over TLS. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “https+unix” scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(httpsURLWithSocketPath socketPath: String, uri: String = "/")
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + uri + + +
    +

    The URI path and query that will be sent to the server.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Protocols.html b/docs/1.9.1/AsyncHTTPClient/Protocols.html new file mode 100644 index 000000000..af66e65f4 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Protocols.html @@ -0,0 +1,230 @@ + + + + Protocols Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Protocols

+

The following protocols are available globally.

+ +
+
+ +
+
+
+
    +
  • + +
    +
    +
    +
    +
    +

    HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed. +You can implement this protocol if you need fine-grained control over an HTTP request/response, for example, if you want to inspect the response +headers before deciding whether to accept a response body, or if you want to stream your request body. Pass an instance of your conforming +class to the HTTPClient.execute() method and this package will call each delegate method appropriately as the request takes place./

    +

    Backpressure

    + +

    A HTTPClientResponseDelegate can be used to exert backpressure on the server response. This is achieved by way of the futures returned from +didReceiveHead and didReceiveBodyPart. The following functions are part of the “backpressure system” in the delegate:

    + +
      +
    • didReceiveHead
    • +
    • didReceiveBodyPart
    • +
    • didFinishRequest
    • +
    • didReceiveError
    • +
    + +

    The first three methods are strictly exclusive, with that exclusivity managed by the futures returned by didReceiveHead and +didReceiveBodyPart. What this means is that until the returned future is completed, none of these three methods will be called +again. This allows delegates to rate limit the server to a capacity it can manage. didFinishRequest does not return a future, +as we are expecting no more data from the server at this time.

    + +

    didReceiveError is somewhat special: it signals the end of this regime. didRecieveError is not exclusive: it may be called at +any time, even if a returned future is not yet completed. didReceiveError is terminal, meaning that once it has been called none +of these four methods will be called again. This can be used as a signal to abandon all outstanding work.

    +
    +

    Note

    + This delegate is strongly held by the HTTPTaskHandler + for the duration of the Request processing and will be + released together with the HTTPTaskHandler when channel is closed. + Users of the library are not required to keep a reference to the + object that implements this protocol, but may do so if needed. + +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public protocol HTTPClientResponseDelegate : AnyObject
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html b/docs/1.9.1/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html new file mode 100644 index 000000000..154fb759c --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html @@ -0,0 +1,712 @@ + + + + HTTPClientResponseDelegate Protocol Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientResponseDelegate

+
+
+ +
public protocol HTTPClientResponseDelegate : AnyObject
+ +
+
+

HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed. +You can implement this protocol if you need fine-grained control over an HTTP request/response, for example, if you want to inspect the response +headers before deciding whether to accept a response body, or if you want to stream your request body. Pass an instance of your conforming +class to the HTTPClient.execute() method and this package will call each delegate method appropriately as the request takes place./

+

Backpressure

+ +

A HTTPClientResponseDelegate can be used to exert backpressure on the server response. This is achieved by way of the futures returned from +didReceiveHead and didReceiveBodyPart. The following functions are part of the “backpressure system” in the delegate:

+ +
    +
  • didReceiveHead
  • +
  • didReceiveBodyPart
  • +
  • didFinishRequest
  • +
  • didReceiveError
  • +
+ +

The first three methods are strictly exclusive, with that exclusivity managed by the futures returned by didReceiveHead and +didReceiveBodyPart. What this means is that until the returned future is completed, none of these three methods will be called +again. This allows delegates to rate limit the server to a capacity it can manage. didFinishRequest does not return a future, +as we are expecting no more data from the server at this time.

+ +

didReceiveError is somewhat special: it signals the end of this regime. didRecieveError is not exclusive: it may be called at +any time, even if a returned future is not yet completed. didReceiveError is terminal, meaning that once it has been called none +of these four methods will be called again. This can be used as a signal to abandon all outstanding work.

+
+

Note

+ This delegate is strongly held by the HTTPTaskHandler + for the duration of the Request processing and will be + released together with the HTTPTaskHandler when channel is closed. + Users of the library are not required to keep a reference to the + object that implements this protocol, but may do so if needed. + +
+ + +
+
+ +
+
+
+
    +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    associatedtype Response
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequestHead(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when the request head is sent. Will be called once.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequestHead(task: HTTPClient.Task<Response>, _ head: HTTPRequestHead)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + head + + +
    +

    Request head.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequestPart(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when a part of the request body is sent. Could be called zero or more times.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequestPart(task: HTTPClient.Task<Response>, _ part: IOData)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + part + + +
    +

    Request body Part.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequest(task:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when the request is fully sent. Will be called once.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequest(task: HTTPClient.Task<Response>)
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveHead(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when response head is received. Will be called once. +You must return an EventLoopFuture<Void> that you complete when you have finished processing the body part. +You can create an already succeeded future by calling task.eventLoop.makeSucceededFuture(()).

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveHead(task: HTTPClient.Task<Response>, _ head: HTTPResponseHead) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + head + + +
    +

    Received reposonse head.

    +
    +
    +
    +
    +

    Return Value

    +

    EventLoopFuture that will be used for backpressure.

    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveBodyPart(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when part of a response body is received. Could be called zero or more times. +You must return an EventLoopFuture<Void> that you complete when you have finished processing the body part. +You can create an already succeeded future by calling task.eventLoop.makeSucceededFuture(()).

    + +

    This function will not be called until the future returned by didReceiveHead has completed.

    + +

    This function will not be called for subsequent body parts until the previous future returned by a +call to this function completes.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveBodyPart(task: HTTPClient.Task<Response>, _ buffer: ByteBuffer) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + buffer + + +
    +

    Received body Part.

    +
    +
    +
    +
    +

    Return Value

    +

    EventLoopFuture that will be used for backpressure.

    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveError(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when error was thrown during request execution. Will be called zero or one time only. Request processing will be stopped after that.

    + +

    This function may be called at any time: it does not respect the backpressure exerted by didReceiveHead and didReceiveBodyPart. +All outstanding work may be cancelled when this is received. Once called, no further calls will be made to didReceiveHead, didReceiveBodyPart, +or didFinishRequest.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveError(task: HTTPClient.Task<Response>, _ error: Error)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + error + + +
    +

    Error that occured during response processing.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Called when the complete HTTP request is finished. You must return an instance of your Response associated type. Will be called once, except if an error occurred.

    + +

    This function will not be called until all futures returned by didReceiveHead and didReceiveBodyPart have completed. Once called, +no further calls will be made to didReceiveHead, didReceiveBodyPart, or didReceiveError.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didFinishRequest(task: HTTPClient.Task<Response>) throws -> Response
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    +
    +
    +

    Return Value

    +

    Result of processing.

    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Structs.html b/docs/1.9.1/AsyncHTTPClient/Structs.html new file mode 100644 index 000000000..7cabbe69f --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Structs.html @@ -0,0 +1,198 @@ + + + + Structures Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Structures

+

The following structures are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + HTTPClientError + +
    +
    +
    +
    +
    +
    +

    Possible client errors.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct HTTPClientError : Error, Equatable, CustomStringConvertible
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/Structs/HTTPClientError.html b/docs/1.9.1/AsyncHTTPClient/Structs/HTTPClientError.html new file mode 100644 index 000000000..61aabb7ce --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/Structs/HTTPClientError.html @@ -0,0 +1,1176 @@ + + + + HTTPClientError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientError

+
+
+ +
public struct HTTPClientError : Error, Equatable, CustomStringConvertible
+ +
+
+

Possible client errors.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var description: String { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + invalidURL + +
    +
    +
    +
    +
    +
    +

    URL provided is invalid.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let invalidURL: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + emptyHost + +
    +
    +
    +
    +
    +
    +

    URL does not contain host.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let emptyHost: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + missingSocketPath + +
    +
    +
    +
    +
    +
    +

    URL does not contain a socketPath as a host for http(s)+unix shemes.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let missingSocketPath: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + alreadyShutdown + +
    +
    +
    +
    +
    +
    +

    Client is shutdown and cannot be used for new requests.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let alreadyShutdown: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + emptyScheme + +
    +
    +
    +
    +
    +
    +

    URL does not contain scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let emptyScheme: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + unsupportedScheme(_:) + +
    +
    +
    +
    +
    +
    +

    Provided URL scheme is not supported, supported schemes are: http and https

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func unsupportedScheme(_ scheme: String) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + readTimeout + +
    +
    +
    +
    +
    +
    +

    Request timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let readTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Remote connection was closed unexpectedly.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let remoteConnectionClosed: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + cancelled + +
    +
    +
    +
    +
    +
    +

    Request was cancelled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let cancelled: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Request contains invalid identity encoding.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let identityCodingIncorrectlyPresent: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Request contains multiple chunks definitions.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    @available(*, deprecated, message: "AsyncHTTPClient now silently corrects this invalid header.")
    +public static let chunkedSpecifiedMultipleTimes: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + invalidProxyResponse + +
    +
    +
    +
    +
    +
    +

    Proxy response was invalid.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let invalidProxyResponse: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + contentLengthMissing + +
    +
    +
    +
    +
    +
    +

    Request does not contain Content-Length header.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let contentLengthMissing: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Proxy Authentication Required.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let proxyAuthenticationRequired: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + redirectLimitReached + +
    +
    +
    +
    +
    +
    +

    Redirect Limit reached.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let redirectLimitReached: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + redirectCycleDetected + +
    +
    +
    +
    +
    +
    +

    Redirect Cycle detected.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let redirectCycleDetected: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + uncleanShutdown + +
    +
    +
    +
    +
    +
    +

    Unclean shutdown.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let uncleanShutdown: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + traceRequestWithBody + +
    +
    +
    +
    +
    +
    +

    A body was sent in a request with method TRACE.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let traceRequestWithBody: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Header field names contain invalid characters.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func invalidHeaderFieldNames(_ names: [String]) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Header field values contain invalid characters.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func invalidHeaderFieldValues(_ values: [String]) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + bodyLengthMismatch + +
    +
    +
    +
    +
    +
    +

    Body length is not equal to Content-Length.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let bodyLengthMismatch: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + writeAfterRequestSent + +
    +
    +
    +
    +
    +
    +

    Body part was written after request was fully sent.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let writeAfterRequestSent: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + incompatibleHeaders + +
    +
    +
    +
    +
    +
    +

    Incompatible headers specified, for example Transfer-Encoding and Content-Length.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    @available(*, deprecated, message: "AsyncHTTPClient now silently corrects invalid headers.")
    +public static let incompatibleHeaders: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + connectTimeout + +
    +
    +
    +
    +
    +
    +

    Creating a new tcp connection timed out

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let connectTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + socksHandshakeTimeout + +
    +
    +
    +
    +
    +
    +

    The socks handshake timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let socksHandshakeTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The http proxy connection creation timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let httpProxyHandshakeTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + tlsHandshakeTimeout + +
    +
    +
    +
    +
    +
    +

    The tls handshake timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let tlsHandshakeTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The remote server only offered an unsupported application protocol

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func serverOfferedUnsupportedApplicationProtocol(_ proto: String) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + deadlineExceeded + +
    +
    +
    +
    +
    +
    +

    The request deadline was exceeded. The request was cancelled because of this.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let deadlineExceeded: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The remote server responded with a status code >= 300, before the full request was sent. The request stream +was therefore cancelled

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let requestStreamCancelled: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Aquiring a HTTP connection from the connection pool timed out.

    + +

    This can have multiple reasons:

    + +
      +
    • A connection could not be created within the timout period.
    • +
    • Tasks are not processed fast enough on the existing connections, to process all waiters in time
    • +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let getConnectionFromPoolTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let httpEndReceivedAfterHeadWith1xx: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/badge.svg b/docs/1.9.1/AsyncHTTPClient/badge.svg new file mode 100644 index 000000000..883955dba --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/badge.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + documentation + + + documentation + + + 87% + + + 87% + + + diff --git a/docs/1.9.1/AsyncHTTPClient/css/highlight.css b/docs/1.9.1/AsyncHTTPClient/css/highlight.css new file mode 100644 index 000000000..c170357ce --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/css/highlight.css @@ -0,0 +1,202 @@ +/*! Jazzy - https://github.com/realm/jazzy + * Copyright Realm Inc. + * SPDX-License-Identifier: MIT + */ +/* Credit to https://gist.github.com/wataru420/2048287 */ +.highlight .c { + color: #999988; + font-style: italic; } + +.highlight .err { + color: #a61717; + background-color: #e3d2d2; } + +.highlight .k { + color: #000000; + font-weight: bold; } + +.highlight .o { + color: #000000; + font-weight: bold; } + +.highlight .cm { + color: #999988; + font-style: italic; } + +.highlight .cp { + color: #999999; + font-weight: bold; } + +.highlight .c1 { + color: #999988; + font-style: italic; } + +.highlight .cs { + color: #999999; + font-weight: bold; + font-style: italic; } + +.highlight .gd { + color: #000000; + background-color: #ffdddd; } + +.highlight .gd .x { + color: #000000; + background-color: #ffaaaa; } + +.highlight .ge { + color: #000000; + font-style: italic; } + +.highlight .gr { + color: #aa0000; } + +.highlight .gh { + color: #999999; } + +.highlight .gi { + color: #000000; + background-color: #ddffdd; } + +.highlight .gi .x { + color: #000000; + background-color: #aaffaa; } + +.highlight .go { + color: #888888; } + +.highlight .gp { + color: #555555; } + +.highlight .gs { + font-weight: bold; } + +.highlight .gu { + color: #aaaaaa; } + +.highlight .gt { + color: #aa0000; } + +.highlight .kc { + color: #000000; + font-weight: bold; } + +.highlight .kd { + color: #000000; + font-weight: bold; } + +.highlight .kp { + color: #000000; + font-weight: bold; } + +.highlight .kr { + color: #000000; + font-weight: bold; } + +.highlight .kt { + color: #445588; } + +.highlight .m { + color: #009999; } + +.highlight .s { + color: #d14; } + +.highlight .na { + color: #008080; } + +.highlight .nb { + color: #0086B3; } + +.highlight .nc { + color: #445588; + font-weight: bold; } + +.highlight .no { + color: #008080; } + +.highlight .ni { + color: #800080; } + +.highlight .ne { + color: #990000; + font-weight: bold; } + +.highlight .nf { + color: #990000; } + +.highlight .nn { + color: #555555; } + +.highlight .nt { + color: #000080; } + +.highlight .nv { + color: #008080; } + +.highlight .ow { + color: #000000; + font-weight: bold; } + +.highlight .w { + color: #bbbbbb; } + +.highlight .mf { + color: #009999; } + +.highlight .mh { + color: #009999; } + +.highlight .mi { + color: #009999; } + +.highlight .mo { + color: #009999; } + +.highlight .sb { + color: #d14; } + +.highlight .sc { + color: #d14; } + +.highlight .sd { + color: #d14; } + +.highlight .s2 { + color: #d14; } + +.highlight .se { + color: #d14; } + +.highlight .sh { + color: #d14; } + +.highlight .si { + color: #d14; } + +.highlight .sx { + color: #d14; } + +.highlight .sr { + color: #009926; } + +.highlight .s1 { + color: #d14; } + +.highlight .ss { + color: #990073; } + +.highlight .bp { + color: #999999; } + +.highlight .vc { + color: #008080; } + +.highlight .vg { + color: #008080; } + +.highlight .vi { + color: #008080; } + +.highlight .il { + color: #009999; } diff --git a/docs/1.9.1/AsyncHTTPClient/css/jazzy.css b/docs/1.9.1/AsyncHTTPClient/css/jazzy.css new file mode 100644 index 000000000..c7bb9fe22 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/css/jazzy.css @@ -0,0 +1,404 @@ +/*! Jazzy - https://github.com/realm/jazzy + * Copyright Realm Inc. + * SPDX-License-Identifier: MIT + */ +*, *:before, *:after { + box-sizing: inherit; } + +body { + margin: 0; + background: #fff; + color: #333; + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + letter-spacing: .2px; + -webkit-font-smoothing: antialiased; + box-sizing: border-box; } + +h1 { + font-size: 2rem; + font-weight: 700; + margin: 1.275em 0 0.6em; } + +h2 { + font-size: 1.75rem; + font-weight: 700; + margin: 1.275em 0 0.3em; } + +h3 { + font-size: 1.5rem; + font-weight: 700; + margin: 1em 0 0.3em; } + +h4 { + font-size: 1.25rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h5 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h6 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; + color: #777; } + +p { + margin: 0 0 1em; } + +ul, ol { + padding: 0 0 0 2em; + margin: 0 0 0.85em; } + +blockquote { + margin: 0 0 0.85em; + padding: 0 15px; + color: #858585; + border-left: 4px solid #e5e5e5; } + +img { + max-width: 100%; } + +a { + color: #4183c4; + text-decoration: none; } + a:hover, a:focus { + outline: 0; + text-decoration: underline; } + a.discouraged { + text-decoration: line-through; } + a.discouraged:hover, a.discouraged:focus { + text-decoration: underline line-through; } + +table { + background: #fff; + width: 100%; + border-collapse: collapse; + border-spacing: 0; + overflow: auto; + margin: 0 0 0.85em; } + +tr:nth-child(2n) { + background-color: #fbfbfb; } + +th, td { + padding: 6px 13px; + border: 1px solid #ddd; } + +hr { + height: 1px; + border: none; + background-color: #ddd; } + +pre { + margin: 0 0 1.275em; + padding: .85em 1em; + overflow: auto; + background: #f7f7f7; + font-size: .85em; + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +code { + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +.item-container p > code, .item-container li > code, .top-matter p > code, .top-matter li > code { + background: #f7f7f7; + padding: .2em; } + .item-container p > code:before, .item-container p > code:after, .item-container li > code:before, .item-container li > code:after, .top-matter p > code:before, .top-matter p > code:after, .top-matter li > code:before, .top-matter li > code:after { + letter-spacing: -.2em; + content: "\00a0"; } + +pre code { + padding: 0; + white-space: pre; } + +.content-wrapper { + display: flex; + flex-direction: column; } + @media (min-width: 768px) { + .content-wrapper { + flex-direction: row; } } +.header { + display: flex; + padding: 8px; + font-size: 0.875em; + background: #444; + color: #999; } + +.header-col { + margin: 0; + padding: 0 8px; } + +.header-col--primary { + flex: 1; } + +.header-link { + color: #fff; } + +.header-icon { + padding-right: 2px; + vertical-align: -3px; + height: 16px; } + +.breadcrumbs { + font-size: 0.875em; + padding: 8px 16px; + margin: 0; + background: #fbfbfb; + border-bottom: 1px solid #ddd; } + +.carat { + height: 10px; + margin: 0 5px; } + +.navigation { + order: 2; } + @media (min-width: 768px) { + .navigation { + order: 1; + width: 25%; + max-width: 300px; + padding-bottom: 64px; + overflow: hidden; + word-wrap: normal; + background: #fbfbfb; + border-right: 1px solid #ddd; } } +.nav-groups { + list-style-type: none; + padding-left: 0; } + +.nav-group-name { + border-bottom: 1px solid #ddd; + padding: 8px 0 8px 16px; } + +.nav-group-name-link { + color: #333; } + +.nav-group-tasks { + margin: 8px 0; + padding: 0 0 0 8px; } + +.nav-group-task { + font-size: 1em; + list-style-type: none; + white-space: nowrap; } + +.nav-group-task-link { + color: #808080; } + +.main-content { + order: 1; } + @media (min-width: 768px) { + .main-content { + order: 2; + flex: 1; + padding-bottom: 60px; } } +.section { + padding: 0 32px; + border-bottom: 1px solid #ddd; } + +.section-content { + max-width: 834px; + margin: 0 auto; + padding: 16px 0; } + +.section-name { + color: #666; + display: block; } + .section-name p { + margin-bottom: inherit; } + +.declaration .highlight { + overflow-x: initial; + padding: 8px 0; + margin: 0; + background-color: transparent; + border: none; } + +.task-group-section { + border-top: 1px solid #ddd; } + +.task-group { + padding-top: 0px; } + +.task-name-container a[name]:before { + content: ""; + display: block; } + +.section-name-container { + position: relative; } + .section-name-container .section-name-link { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + margin-bottom: 0; } + .section-name-container .section-name { + position: relative; + pointer-events: none; + z-index: 1; } + .section-name-container .section-name a { + pointer-events: auto; } + +.item-container { + padding: 0; } + +.item { + padding-top: 8px; + width: 100%; + list-style-type: none; } + .item a[name]:before { + content: ""; + display: block; } + .item .token, .item .direct-link { + display: inline-block; + text-indent: -20px; + padding-left: 3px; + margin-left: 20px; + font-size: 1rem; } + .item .declaration-note { + font-size: .85em; + color: #808080; + font-style: italic; } + +.pointer-container { + border-bottom: 1px solid #ddd; + left: -23px; + padding-bottom: 13px; + position: relative; + width: 110%; } + +.pointer { + left: 21px; + top: 7px; + display: block; + position: absolute; + width: 12px; + height: 12px; + border-left: 1px solid #ddd; + border-top: 1px solid #ddd; + background: #fff; + transform: rotate(45deg); } + +.height-container { + display: none; + position: relative; + width: 100%; + overflow: hidden; } + .height-container .section { + background: #fff; + border: 1px solid #ddd; + border-top-width: 0; + padding-top: 10px; + padding-bottom: 5px; + padding: 8px 16px; } + +.aside, .language { + padding: 6px 12px; + margin: 12px 0; + border-left: 5px solid #dddddd; + overflow-y: hidden; } + .aside .aside-title, .language .aside-title { + font-size: 9px; + letter-spacing: 2px; + text-transform: uppercase; + padding-bottom: 0; + margin: 0; + color: #aaa; + -webkit-user-select: none; } + .aside p:last-child, .language p:last-child { + margin-bottom: 0; } + +.language { + border-left: 5px solid #cde9f4; } + .language .aside-title { + color: #4183c4; } + +.aside-warning, .aside-deprecated, .aside-unavailable { + border-left: 5px solid #ff6666; } + .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { + color: #ff0000; } + +.graybox { + border-collapse: collapse; + width: 100%; } + .graybox p { + margin: 0; + word-break: break-word; + min-width: 50px; } + .graybox td { + border: 1px solid #ddd; + padding: 5px 25px 5px 10px; + vertical-align: middle; } + .graybox tr td:first-of-type { + text-align: right; + padding: 7px; + vertical-align: top; + word-break: normal; + width: 40px; } + +.slightly-smaller { + font-size: 0.9em; } + +.footer { + padding: 8px 16px; + background: #444; + color: #ddd; + font-size: 0.8em; } + .footer p { + margin: 8px 0; } + .footer a { + color: #fff; } + +html.dash .header, html.dash .breadcrumbs, html.dash .navigation { + display: none; } + +html.dash .height-container { + display: block; } + +form[role=search] input { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 24px; + padding: 0 10px; + margin: 0; + border: none; + border-radius: 1em; } + .loading form[role=search] input { + background: white url(http://github.com/img/spinner.gif) center right 4px no-repeat; } + +form[role=search] .tt-menu { + margin: 0; + min-width: 300px; + background: #fbfbfb; + color: #333; + border: 1px solid #ddd; } + +form[role=search] .tt-highlight { + font-weight: bold; } + +form[role=search] .tt-suggestion { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 0 8px; } + form[role=search] .tt-suggestion span { + display: table-cell; + white-space: nowrap; } + form[role=search] .tt-suggestion .doc-parent-name { + width: 100%; + text-align: right; + font-weight: normal; + font-size: 0.9em; + padding-left: 16px; } + +form[role=search] .tt-suggestion:hover, +form[role=search] .tt-suggestion.tt-cursor { + cursor: pointer; + background-color: #4183c4; + color: #fff; } + +form[role=search] .tt-suggestion:hover .doc-parent-name, +form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { + color: #fff; } diff --git a/docs/1.9.1/AsyncHTTPClient/img/carat.png b/docs/1.9.1/AsyncHTTPClient/img/carat.png new file mode 100755 index 000000000..29d2f7fd4 Binary files /dev/null and b/docs/1.9.1/AsyncHTTPClient/img/carat.png differ diff --git a/docs/1.9.1/AsyncHTTPClient/img/dash.png b/docs/1.9.1/AsyncHTTPClient/img/dash.png new file mode 100755 index 000000000..6f694c7a0 Binary files /dev/null and b/docs/1.9.1/AsyncHTTPClient/img/dash.png differ diff --git a/docs/1.9.1/AsyncHTTPClient/img/gh.png b/docs/1.9.1/AsyncHTTPClient/img/gh.png new file mode 100755 index 000000000..628da97c7 Binary files /dev/null and b/docs/1.9.1/AsyncHTTPClient/img/gh.png differ diff --git a/docs/1.9.1/AsyncHTTPClient/img/spinner.gif b/docs/1.9.1/AsyncHTTPClient/img/spinner.gif new file mode 100644 index 000000000..e3038d0a4 Binary files /dev/null and b/docs/1.9.1/AsyncHTTPClient/img/spinner.gif differ diff --git a/docs/1.9.1/AsyncHTTPClient/index.html b/docs/1.9.1/AsyncHTTPClient/index.html new file mode 100644 index 000000000..b17a4215a --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/index.html @@ -0,0 +1,164 @@ + + + + AsyncHTTPClient Reference + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.9.1 Docs + + (87% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+ +

AsyncHTTPClient Docs

+ +

AsyncHTTPClient is a Swift HTTP Client package.

+ +

To get started with AsyncHTTPClient, import AsyncHTTPClient. The +most important type is HTTPClient +which you can use to emit log messages.

+ +
+
+ + +
+
+ + + diff --git a/docs/1.9.1/AsyncHTTPClient/js/jazzy.js b/docs/1.9.1/AsyncHTTPClient/js/jazzy.js new file mode 100755 index 000000000..198441660 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/js/jazzy.js @@ -0,0 +1,74 @@ +// Jazzy - https://github.com/realm/jazzy +// Copyright Realm Inc. +// SPDX-License-Identifier: MIT + +window.jazzy = {'docset': false} +if (typeof window.dash != 'undefined') { + document.documentElement.className += ' dash' + window.jazzy.docset = true +} +if (navigator.userAgent.match(/xcode/i)) { + document.documentElement.className += ' xcode' + window.jazzy.docset = true +} + +function toggleItem($link, $content) { + var animationDuration = 300; + $link.toggleClass('token-open'); + $content.slideToggle(animationDuration); +} + +function itemLinkToContent($link) { + return $link.parent().parent().next(); +} + +// On doc load + hash-change, open any targetted item +function openCurrentItemIfClosed() { + if (window.jazzy.docset) { + return; + } + var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); + $content = itemLinkToContent($link); + if ($content.is(':hidden')) { + toggleItem($link, $content); + } +} + +$(openCurrentItemIfClosed); +$(window).on('hashchange', openCurrentItemIfClosed); + +// On item link ('token') click, toggle its discussion +$('.token').on('click', function(event) { + if (window.jazzy.docset) { + return; + } + var $link = $(this); + toggleItem($link, itemLinkToContent($link)); + + // Keeps the document from jumping to the hash. + var href = $link.attr('href'); + if (history.pushState) { + history.pushState({}, '', href); + } else { + location.hash = href; + } + event.preventDefault(); +}); + +// Clicks on links to the current, closed, item need to open the item +$("a:not('.token')").on('click', function() { + if (location == this.href) { + openCurrentItemIfClosed(); + } +}); + +// KaTeX rendering +if ("katex" in window) { + $($('.math').each( (_, element) => { + katex.render(element.textContent, element, { + displayMode: $(element).hasClass('m-block'), + throwOnError: false, + trust: true + }); + })) +} diff --git a/docs/1.9.1/AsyncHTTPClient/js/jazzy.search.js b/docs/1.9.1/AsyncHTTPClient/js/jazzy.search.js new file mode 100644 index 000000000..359cdbb8b --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/js/jazzy.search.js @@ -0,0 +1,74 @@ +// Jazzy - https://github.com/realm/jazzy +// Copyright Realm Inc. +// SPDX-License-Identifier: MIT + +$(function(){ + var $typeahead = $('[data-typeahead]'); + var $form = $typeahead.parents('form'); + var searchURL = $form.attr('action'); + + function displayTemplate(result) { + return result.name; + } + + function suggestionTemplate(result) { + var t = '
'; + t += '' + result.name + ''; + if (result.parent_name) { + t += '' + result.parent_name + ''; + } + t += '
'; + return t; + } + + $typeahead.one('focus', function() { + $form.addClass('loading'); + + $.getJSON(searchURL).then(function(searchData) { + const searchIndex = lunr(function() { + this.ref('url'); + this.field('name'); + this.field('abstract'); + for (const [url, doc] of Object.entries(searchData)) { + this.add({url: url, name: doc.name, abstract: doc.abstract}); + } + }); + + $typeahead.typeahead( + { + highlight: true, + minLength: 3, + autoselect: true + }, + { + limit: 10, + display: displayTemplate, + templates: { suggestion: suggestionTemplate }, + source: function(query, sync) { + const lcSearch = query.toLowerCase(); + const results = searchIndex.query(function(q) { + q.term(lcSearch, { boost: 100 }); + q.term(lcSearch, { + boost: 10, + wildcard: lunr.Query.wildcard.TRAILING + }); + }).map(function(result) { + var doc = searchData[result.ref]; + doc.url = result.ref; + return doc; + }); + sync(results); + } + } + ); + $form.removeClass('loading'); + $typeahead.trigger('focus'); + }); + }); + + var baseURL = searchURL.slice(0, -"search.json".length); + + $typeahead.on('typeahead:select', function(e, result) { + window.location = baseURL + result.url; + }); +}); diff --git a/docs/1.9.1/AsyncHTTPClient/js/jquery.min.js b/docs/1.9.1/AsyncHTTPClient/js/jquery.min.js new file mode 100644 index 000000000..2c69bc908 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.1 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,y=n.hasOwnProperty,a=y.toString,l=a.call(Object),v={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&v(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!y||!y.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ve(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ye(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ve(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],y=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||y.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||y.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||y.push(".#.+[+~]"),e.querySelectorAll("\\\f"),y.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),y=y.length&&new RegExp(y.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),v=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&v(p,e)?-1:t==C||t.ownerDocument==p&&v(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!y||!y.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),v.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",v.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",v.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),v.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(v.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return B(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=_e(v.pixelPosition,function(e,t){if(t)return t=Be(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return B(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 00){var c=e.utils.clone(r)||{};c.position=[a,l],c.index=s.length,s.push(new e.Token(i.slice(a,o),c))}a=o+1}}return s},e.tokenizer.separator=/[\s\-]+/,e.Pipeline=function(){this._stack=[]},e.Pipeline.registeredFunctions=Object.create(null),e.Pipeline.registerFunction=function(t,r){r in this.registeredFunctions&&e.utils.warn("Overwriting existing registered function: "+r),t.label=r,e.Pipeline.registeredFunctions[t.label]=t},e.Pipeline.warnIfFunctionNotRegistered=function(t){var r=t.label&&t.label in this.registeredFunctions;r||e.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",t)},e.Pipeline.load=function(t){var r=new e.Pipeline;return t.forEach(function(t){var i=e.Pipeline.registeredFunctions[t];if(!i)throw new Error("Cannot load unregistered function: "+t);r.add(i)}),r},e.Pipeline.prototype.add=function(){var t=Array.prototype.slice.call(arguments);t.forEach(function(t){e.Pipeline.warnIfFunctionNotRegistered(t),this._stack.push(t)},this)},e.Pipeline.prototype.after=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,r)},e.Pipeline.prototype.before=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");this._stack.splice(i,0,r)},e.Pipeline.prototype.remove=function(e){var t=this._stack.indexOf(e);t!=-1&&this._stack.splice(t,1)},e.Pipeline.prototype.run=function(e){for(var t=this._stack.length,r=0;r1&&(se&&(r=n),s!=e);)i=r-t,n=t+Math.floor(i/2),s=this.elements[2*n];return s==e?2*n:s>e?2*n:sa?l+=2:o==a&&(t+=r[u+1]*i[l+1],u+=2,l+=2);return t},e.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},e.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),t=1,r=0;t0){var o,a=s.str.charAt(0);a in s.node.edges?o=s.node.edges[a]:(o=new e.TokenSet,s.node.edges[a]=o),1==s.str.length&&(o["final"]=!0),n.push({node:o,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(0!=s.editsRemaining){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new e.TokenSet;s.node.edges["*"]=u}if(0==s.str.length&&(u["final"]=!0),n.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&n.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),1==s.str.length&&(s.node["final"]=!0),s.str.length>=1){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new e.TokenSet;s.node.edges["*"]=l}1==s.str.length&&(l["final"]=!0),n.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var c,h=s.str.charAt(0),d=s.str.charAt(1);d in s.node.edges?c=s.node.edges[d]:(c=new e.TokenSet,s.node.edges[d]=c),1==s.str.length&&(c["final"]=!0),n.push({node:c,editsRemaining:s.editsRemaining-1,str:h+s.str.slice(2)})}}}return i},e.TokenSet.fromString=function(t){for(var r=new e.TokenSet,i=r,n=0,s=t.length;n=e;t--){var r=this.uncheckedNodes[t],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r["char"]]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}},e.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},e.Index.prototype.search=function(t){return this.query(function(r){var i=new e.QueryParser(t,r);i.parse()})},e.Index.prototype.query=function(t){for(var r=new e.Query(this.fields),i=Object.create(null),n=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),u=0;u1?this._b=1:this._b=e},e.Builder.prototype.k1=function(e){this._k1=e},e.Builder.prototype.add=function(t,r){var i=t[this._ref],n=Object.keys(this._fields);this._documents[i]=r||{},this.documentCount+=1;for(var s=0;s=this.length)return e.QueryLexer.EOS;var t=this.str.charAt(this.pos);return this.pos+=1,t},e.QueryLexer.prototype.width=function(){return this.pos-this.start},e.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},e.QueryLexer.prototype.backup=function(){this.pos-=1},e.QueryLexer.prototype.acceptDigitRun=function(){var t,r;do t=this.next(),r=t.charCodeAt(0);while(r>47&&r<58);t!=e.QueryLexer.EOS&&this.backup()},e.QueryLexer.prototype.more=function(){return this.pos1&&(t.backup(),t.emit(e.QueryLexer.TERM)),t.ignore(),t.more())return e.QueryLexer.lexText},e.QueryLexer.lexEditDistance=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.EDIT_DISTANCE),e.QueryLexer.lexText},e.QueryLexer.lexBoost=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.BOOST),e.QueryLexer.lexText},e.QueryLexer.lexEOS=function(t){t.width()>0&&t.emit(e.QueryLexer.TERM)},e.QueryLexer.termSeparator=e.tokenizer.separator,e.QueryLexer.lexText=function(t){for(;;){var r=t.next();if(r==e.QueryLexer.EOS)return e.QueryLexer.lexEOS;if(92!=r.charCodeAt(0)){if(":"==r)return e.QueryLexer.lexField;if("~"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexEditDistance;if("^"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexBoost;if("+"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if("-"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if(r.match(e.QueryLexer.termSeparator))return e.QueryLexer.lexTerm}else t.escapeCharacter()}},e.QueryParser=function(t,r){this.lexer=new e.QueryLexer(t),this.query=r,this.currentClause={},this.lexemeIdx=0},e.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var t=e.QueryParser.parseClause;t;)t=t(this);return this.query},e.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},e.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},e.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},e.QueryParser.parseClause=function(t){var r=t.peekLexeme();if(void 0!=r)switch(r.type){case e.QueryLexer.PRESENCE:return e.QueryParser.parsePresence;case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expected either a field or a term, found "+r.type;throw r.str.length>=1&&(i+=" with value '"+r.str+"'"),new e.QueryParseError(i,r.start,r.end)}},e.QueryParser.parsePresence=function(t){var r=t.consumeLexeme();if(void 0!=r){switch(r.str){case"-":t.currentClause.presence=e.Query.presence.PROHIBITED;break;case"+":t.currentClause.presence=e.Query.presence.REQUIRED;break;default:var i="unrecognised presence operator'"+r.str+"'";throw new e.QueryParseError(i,r.start,r.end)}var n=t.peekLexeme();if(void 0==n){var i="expecting term or field, found nothing";throw new e.QueryParseError(i,r.start,r.end)}switch(n.type){case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expecting term or field, found '"+n.type+"'";throw new e.QueryParseError(i,n.start,n.end)}}},e.QueryParser.parseField=function(t){var r=t.consumeLexeme();if(void 0!=r){if(t.query.allFields.indexOf(r.str)==-1){var i=t.query.allFields.map(function(e){return"'"+e+"'"}).join(", "),n="unrecognised field '"+r.str+"', possible fields: "+i;throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.fields=[r.str];var s=t.peekLexeme();if(void 0==s){var n="expecting term, found nothing";throw new e.QueryParseError(n,r.start,r.end)}switch(s.type){case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var n="expecting term, found '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseTerm=function(t){var r=t.consumeLexeme();if(void 0!=r){t.currentClause.term=r.str.toLowerCase(),r.str.indexOf("*")!=-1&&(t.currentClause.usePipeline=!1);var i=t.peekLexeme();if(void 0==i)return void t.nextClause();switch(i.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+i.type+"'";throw new e.QueryParseError(n,i.start,i.end)}}},e.QueryParser.parseEditDistance=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="edit distance must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.editDistance=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseBoost=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="boost must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.boost=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():e.lunr=t()}(this,function(){return e})}(); diff --git a/docs/1.9.1/AsyncHTTPClient/js/typeahead.jquery.js b/docs/1.9.1/AsyncHTTPClient/js/typeahead.jquery.js new file mode 100644 index 000000000..3a2d2ab03 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/js/typeahead.jquery.js @@ -0,0 +1,1694 @@ +/*! + * typeahead.js 1.3.1 + * https://github.com/corejavascript/typeahead.js + * Copyright 2013-2020 Twitter, Inc. and other contributors; Licensed MIT + */ + + +(function(root, factory) { + if (typeof define === "function" && define.amd) { + define([ "jquery" ], function(a0) { + return factory(a0); + }); + } else if (typeof module === "object" && module.exports) { + module.exports = factory(require("jquery")); + } else { + factory(root["jQuery"]); + } +})(this, function($) { + var _ = function() { + "use strict"; + return { + isMsie: function() { + return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false; + }, + isBlankString: function(str) { + return !str || /^\s*$/.test(str); + }, + escapeRegExChars: function(str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + }, + isString: function(obj) { + return typeof obj === "string"; + }, + isNumber: function(obj) { + return typeof obj === "number"; + }, + isArray: $.isArray, + isFunction: $.isFunction, + isObject: $.isPlainObject, + isUndefined: function(obj) { + return typeof obj === "undefined"; + }, + isElement: function(obj) { + return !!(obj && obj.nodeType === 1); + }, + isJQuery: function(obj) { + return obj instanceof $; + }, + toStr: function toStr(s) { + return _.isUndefined(s) || s === null ? "" : s + ""; + }, + bind: $.proxy, + each: function(collection, cb) { + $.each(collection, reverseArgs); + function reverseArgs(index, value) { + return cb(value, index); + } + }, + map: $.map, + filter: $.grep, + every: function(obj, test) { + var result = true; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (!(result = test.call(null, val, key, obj))) { + return false; + } + }); + return !!result; + }, + some: function(obj, test) { + var result = false; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (result = test.call(null, val, key, obj)) { + return false; + } + }); + return !!result; + }, + mixin: $.extend, + identity: function(x) { + return x; + }, + clone: function(obj) { + return $.extend(true, {}, obj); + }, + getIdGenerator: function() { + var counter = 0; + return function() { + return counter++; + }; + }, + templatify: function templatify(obj) { + return $.isFunction(obj) ? obj : template; + function template() { + return String(obj); + } + }, + defer: function(fn) { + setTimeout(fn, 0); + }, + debounce: function(func, wait, immediate) { + var timeout, result; + return function() { + var context = this, args = arguments, later, callNow; + later = function() { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + } + }; + callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + } + return result; + }; + }, + throttle: function(func, wait) { + var context, args, timeout, result, previous, later; + previous = 0; + later = function() { + previous = new Date(); + timeout = null; + result = func.apply(context, args); + }; + return function() { + var now = new Date(), remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + } else if (!timeout) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }, + stringify: function(val) { + return _.isString(val) ? val : JSON.stringify(val); + }, + guid: function() { + function _p8(s) { + var p = (Math.random().toString(16) + "000000000").substr(2, 8); + return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p; + } + return "tt-" + _p8() + _p8(true) + _p8(true) + _p8(); + }, + noop: function() {} + }; + }(); + var WWW = function() { + "use strict"; + var defaultClassNames = { + wrapper: "twitter-typeahead", + input: "tt-input", + hint: "tt-hint", + menu: "tt-menu", + dataset: "tt-dataset", + suggestion: "tt-suggestion", + selectable: "tt-selectable", + empty: "tt-empty", + open: "tt-open", + cursor: "tt-cursor", + highlight: "tt-highlight" + }; + return build; + function build(o) { + var www, classes; + classes = _.mixin({}, defaultClassNames, o); + www = { + css: buildCss(), + classes: classes, + html: buildHtml(classes), + selectors: buildSelectors(classes) + }; + return { + css: www.css, + html: www.html, + classes: www.classes, + selectors: www.selectors, + mixin: function(o) { + _.mixin(o, www); + } + }; + } + function buildHtml(c) { + return { + wrapper: '', + menu: '
' + }; + } + function buildSelectors(classes) { + var selectors = {}; + _.each(classes, function(v, k) { + selectors[k] = "." + v; + }); + return selectors; + } + function buildCss() { + var css = { + wrapper: { + position: "relative", + display: "inline-block" + }, + hint: { + position: "absolute", + top: "0", + left: "0", + borderColor: "transparent", + boxShadow: "none", + opacity: "1" + }, + input: { + position: "relative", + verticalAlign: "top", + backgroundColor: "transparent" + }, + inputWithNoHint: { + position: "relative", + verticalAlign: "top" + }, + menu: { + position: "absolute", + top: "100%", + left: "0", + zIndex: "100", + display: "none" + }, + ltr: { + left: "0", + right: "auto" + }, + rtl: { + left: "auto", + right: " 0" + } + }; + if (_.isMsie()) { + _.mixin(css.input, { + backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" + }); + } + return css; + } + }(); + var EventBus = function() { + "use strict"; + var namespace, deprecationMap; + namespace = "typeahead:"; + deprecationMap = { + render: "rendered", + cursorchange: "cursorchanged", + select: "selected", + autocomplete: "autocompleted" + }; + function EventBus(o) { + if (!o || !o.el) { + $.error("EventBus initialized without el"); + } + this.$el = $(o.el); + } + _.mixin(EventBus.prototype, { + _trigger: function(type, args) { + var $e = $.Event(namespace + type); + this.$el.trigger.call(this.$el, $e, args || []); + return $e; + }, + before: function(type) { + var args, $e; + args = [].slice.call(arguments, 1); + $e = this._trigger("before" + type, args); + return $e.isDefaultPrevented(); + }, + trigger: function(type) { + var deprecatedType; + this._trigger(type, [].slice.call(arguments, 1)); + if (deprecatedType = deprecationMap[type]) { + this._trigger(deprecatedType, [].slice.call(arguments, 1)); + } + } + }); + return EventBus; + }(); + var EventEmitter = function() { + "use strict"; + var splitter = /\s+/, nextTick = getNextTick(); + return { + onSync: onSync, + onAsync: onAsync, + off: off, + trigger: trigger + }; + function on(method, types, cb, context) { + var type; + if (!cb) { + return this; + } + types = types.split(splitter); + cb = context ? bindContext(cb, context) : cb; + this._callbacks = this._callbacks || {}; + while (type = types.shift()) { + this._callbacks[type] = this._callbacks[type] || { + sync: [], + async: [] + }; + this._callbacks[type][method].push(cb); + } + return this; + } + function onAsync(types, cb, context) { + return on.call(this, "async", types, cb, context); + } + function onSync(types, cb, context) { + return on.call(this, "sync", types, cb, context); + } + function off(types) { + var type; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + while (type = types.shift()) { + delete this._callbacks[type]; + } + return this; + } + function trigger(types) { + var type, callbacks, args, syncFlush, asyncFlush; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + args = [].slice.call(arguments, 1); + while ((type = types.shift()) && (callbacks = this._callbacks[type])) { + syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args)); + asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args)); + syncFlush() && nextTick(asyncFlush); + } + return this; + } + function getFlush(callbacks, context, args) { + return flush; + function flush() { + var cancelled; + for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) { + cancelled = callbacks[i].apply(context, args) === false; + } + return !cancelled; + } + } + function getNextTick() { + var nextTickFn; + if (window.setImmediate) { + nextTickFn = function nextTickSetImmediate(fn) { + setImmediate(function() { + fn(); + }); + }; + } else { + nextTickFn = function nextTickSetTimeout(fn) { + setTimeout(function() { + fn(); + }, 0); + }; + } + return nextTickFn; + } + function bindContext(fn, context) { + return fn.bind ? fn.bind(context) : function() { + fn.apply(context, [].slice.call(arguments, 0)); + }; + } + }(); + var highlight = function(doc) { + "use strict"; + var defaults = { + node: null, + pattern: null, + tagName: "strong", + className: null, + wordsOnly: false, + caseSensitive: false, + diacriticInsensitive: false + }; + var accented = { + A: "[AaªÀ-Åà-åĀ-ąǍǎȀ-ȃȦȧᴬᵃḀḁẚẠ-ảₐ℀℁℻⒜Ⓐⓐ㍱-㍴㎀-㎄㎈㎉㎩-㎯㏂㏊㏟㏿Aa]", + B: "[BbᴮᵇḂ-ḇℬ⒝Ⓑⓑ㍴㎅-㎇㏃㏈㏔㏝Bb]", + C: "[CcÇçĆ-čᶜ℀ℂ℃℅℆ℭⅭⅽ⒞Ⓒⓒ㍶㎈㎉㎝㎠㎤㏄-㏇Cc]", + D: "[DdĎďDŽ-džDZ-dzᴰᵈḊ-ḓⅅⅆⅮⅾ⒟Ⓓⓓ㋏㍲㍷-㍹㎗㎭-㎯㏅㏈Dd]", + E: "[EeÈ-Ëè-ëĒ-ěȄ-ȇȨȩᴱᵉḘ-ḛẸ-ẽₑ℡ℯℰⅇ⒠Ⓔⓔ㉐㋍㋎Ee]", + F: "[FfᶠḞḟ℉ℱ℻⒡Ⓕⓕ㎊-㎌㎙ff-fflFf]", + G: "[GgĜ-ģǦǧǴǵᴳᵍḠḡℊ⒢Ⓖⓖ㋌㋍㎇㎍-㎏㎓㎬㏆㏉㏒㏿Gg]", + H: "[HhĤĥȞȟʰᴴḢ-ḫẖℋ-ℎ⒣Ⓗⓗ㋌㍱㎐-㎔㏊㏋㏗Hh]", + I: "[IiÌ-Ïì-ïĨ-İIJijǏǐȈ-ȋᴵᵢḬḭỈ-ịⁱℐℑℹⅈⅠ-ⅣⅥ-ⅨⅪⅫⅰ-ⅳⅵ-ⅸⅺⅻ⒤Ⓘⓘ㍺㏌㏕fiffiIi]", + J: "[JjIJ-ĵLJ-njǰʲᴶⅉ⒥ⒿⓙⱼJj]", + K: "[KkĶķǨǩᴷᵏḰ-ḵK⒦Ⓚⓚ㎄㎅㎉㎏㎑㎘㎞㎢㎦㎪㎸㎾㏀㏆㏍-㏏Kk]", + L: "[LlĹ-ŀLJ-ljˡᴸḶḷḺ-ḽℒℓ℡Ⅼⅼ⒧Ⓛⓛ㋏㎈㎉㏐-㏓㏕㏖㏿flfflLl]", + M: "[MmᴹᵐḾ-ṃ℠™ℳⅯⅿ⒨Ⓜⓜ㍷-㍹㎃㎆㎎㎒㎖㎙-㎨㎫㎳㎷㎹㎽㎿㏁㏂㏎㏐㏔-㏖㏘㏙㏞㏟Mm]", + N: "[NnÑñŃ-ʼnNJ-njǸǹᴺṄ-ṋⁿℕ№⒩Ⓝⓝ㎁㎋㎚㎱㎵㎻㏌㏑Nn]", + O: "[OoºÒ-Öò-öŌ-őƠơǑǒǪǫȌ-ȏȮȯᴼᵒỌ-ỏₒ℅№ℴ⒪Ⓞⓞ㍵㏇㏒㏖Oo]", + P: "[PpᴾᵖṔ-ṗℙ⒫Ⓟⓟ㉐㍱㍶㎀㎊㎩-㎬㎰㎴㎺㏋㏗-㏚Pp]", + Q: "[Qqℚ⒬Ⓠⓠ㏃Qq]", + R: "[RrŔ-řȐ-ȓʳᴿᵣṘ-ṛṞṟ₨ℛ-ℝ⒭Ⓡⓡ㋍㍴㎭-㎯㏚㏛Rr]", + S: "[SsŚ-šſȘșˢṠ-ṣ₨℁℠⒮Ⓢⓢ㎧㎨㎮-㎳㏛㏜stSs]", + T: "[TtŢ-ťȚțᵀᵗṪ-ṱẗ℡™⒯Ⓣⓣ㉐㋏㎔㏏ſtstTt]", + U: "[UuÙ-Üù-üŨ-ųƯưǓǔȔ-ȗᵁᵘᵤṲ-ṷỤ-ủ℆⒰Ⓤⓤ㍳㍺Uu]", + V: "[VvᵛᵥṼ-ṿⅣ-Ⅷⅳ-ⅷ⒱Ⓥⓥⱽ㋎㍵㎴-㎹㏜㏞Vv]", + W: "[WwŴŵʷᵂẀ-ẉẘ⒲Ⓦⓦ㎺-㎿㏝Ww]", + X: "[XxˣẊ-ẍₓ℻Ⅸ-Ⅻⅸ-ⅻ⒳Ⓧⓧ㏓Xx]", + Y: "[YyÝýÿŶ-ŸȲȳʸẎẏẙỲ-ỹ⒴Ⓨⓨ㏉Yy]", + Z: "[ZzŹ-žDZ-dzᶻẐ-ẕℤℨ⒵Ⓩⓩ㎐-㎔Zz]" + }; + return function hightlight(o) { + var regex; + o = _.mixin({}, defaults, o); + if (!o.node || !o.pattern) { + return; + } + o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; + regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly, o.diacriticInsensitive); + traverse(o.node, hightlightTextNode); + function hightlightTextNode(textNode) { + var match, patternNode, wrapperNode; + if (match = regex.exec(textNode.data)) { + wrapperNode = doc.createElement(o.tagName); + o.className && (wrapperNode.className = o.className); + patternNode = textNode.splitText(match.index); + patternNode.splitText(match[0].length); + wrapperNode.appendChild(patternNode.cloneNode(true)); + textNode.parentNode.replaceChild(wrapperNode, patternNode); + } + return !!match; + } + function traverse(el, hightlightTextNode) { + var childNode, TEXT_NODE_TYPE = 3; + for (var i = 0; i < el.childNodes.length; i++) { + childNode = el.childNodes[i]; + if (childNode.nodeType === TEXT_NODE_TYPE) { + i += hightlightTextNode(childNode) ? 1 : 0; + } else { + traverse(childNode, hightlightTextNode); + } + } + } + }; + function accent_replacer(chr) { + return accented[chr.toUpperCase()] || chr; + } + function getRegex(patterns, caseSensitive, wordsOnly, diacriticInsensitive) { + var escapedPatterns = [], regexStr; + for (var i = 0, len = patterns.length; i < len; i++) { + var escapedWord = _.escapeRegExChars(patterns[i]); + if (diacriticInsensitive) { + escapedWord = escapedWord.replace(/\S/g, accent_replacer); + } + escapedPatterns.push(escapedWord); + } + regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")"; + return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); + } + }(window.document); + var Input = function() { + "use strict"; + var specialKeyCodeMap; + specialKeyCodeMap = { + 9: "tab", + 27: "esc", + 37: "left", + 39: "right", + 13: "enter", + 38: "up", + 40: "down" + }; + function Input(o, www) { + var id; + o = o || {}; + if (!o.input) { + $.error("input is missing"); + } + www.mixin(this); + this.$hint = $(o.hint); + this.$input = $(o.input); + this.$menu = $(o.menu); + id = this.$input.attr("id") || _.guid(); + this.$menu.attr("id", id + "_listbox"); + this.$hint.attr({ + "aria-hidden": true + }); + this.$input.attr({ + "aria-owns": id + "_listbox", + role: "combobox", + "aria-autocomplete": "list", + "aria-expanded": false + }); + this.query = this.$input.val(); + this.queryWhenFocused = this.hasFocus() ? this.query : null; + this.$overflowHelper = buildOverflowHelper(this.$input); + this._checkLanguageDirection(); + if (this.$hint.length === 0) { + this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; + } + this.onSync("cursorchange", this._updateDescendent); + } + Input.normalizeQuery = function(str) { + return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " "); + }; + _.mixin(Input.prototype, EventEmitter, { + _onBlur: function onBlur() { + this.resetInputValue(); + this.trigger("blurred"); + }, + _onFocus: function onFocus() { + this.queryWhenFocused = this.query; + this.trigger("focused"); + }, + _onKeydown: function onKeydown($e) { + var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; + this._managePreventDefault(keyName, $e); + if (keyName && this._shouldTrigger(keyName, $e)) { + this.trigger(keyName + "Keyed", $e); + } + }, + _onInput: function onInput() { + this._setQuery(this.getInputValue()); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + _managePreventDefault: function managePreventDefault(keyName, $e) { + var preventDefault; + switch (keyName) { + case "up": + case "down": + preventDefault = !withModifier($e); + break; + + default: + preventDefault = false; + } + preventDefault && $e.preventDefault(); + }, + _shouldTrigger: function shouldTrigger(keyName, $e) { + var trigger; + switch (keyName) { + case "tab": + trigger = !withModifier($e); + break; + + default: + trigger = true; + } + return trigger; + }, + _checkLanguageDirection: function checkLanguageDirection() { + var dir = (this.$input.css("direction") || "ltr").toLowerCase(); + if (this.dir !== dir) { + this.dir = dir; + this.$hint.attr("dir", dir); + this.trigger("langDirChanged", dir); + } + }, + _setQuery: function setQuery(val, silent) { + var areEquivalent, hasDifferentWhitespace; + areEquivalent = areQueriesEquivalent(val, this.query); + hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false; + this.query = val; + if (!silent && !areEquivalent) { + this.trigger("queryChanged", this.query); + } else if (!silent && hasDifferentWhitespace) { + this.trigger("whitespaceChanged", this.query); + } + }, + _updateDescendent: function updateDescendent(event, id) { + this.$input.attr("aria-activedescendant", id); + }, + bind: function() { + var that = this, onBlur, onFocus, onKeydown, onInput; + onBlur = _.bind(this._onBlur, this); + onFocus = _.bind(this._onFocus, this); + onKeydown = _.bind(this._onKeydown, this); + onInput = _.bind(this._onInput, this); + this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown); + if (!_.isMsie() || _.isMsie() > 9) { + this.$input.on("input.tt", onInput); + } else { + this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { + if (specialKeyCodeMap[$e.which || $e.keyCode]) { + return; + } + _.defer(_.bind(that._onInput, that, $e)); + }); + } + return this; + }, + focus: function focus() { + this.$input.focus(); + }, + blur: function blur() { + this.$input.blur(); + }, + getLangDir: function getLangDir() { + return this.dir; + }, + getQuery: function getQuery() { + return this.query || ""; + }, + setQuery: function setQuery(val, silent) { + this.setInputValue(val); + this._setQuery(val, silent); + }, + hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() { + return this.query !== this.queryWhenFocused; + }, + getInputValue: function getInputValue() { + return this.$input.val(); + }, + setInputValue: function setInputValue(value) { + this.$input.val(value); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + resetInputValue: function resetInputValue() { + this.setInputValue(this.query); + }, + getHint: function getHint() { + return this.$hint.val(); + }, + setHint: function setHint(value) { + this.$hint.val(value); + }, + clearHint: function clearHint() { + this.setHint(""); + }, + clearHintIfInvalid: function clearHintIfInvalid() { + var val, hint, valIsPrefixOfHint, isValid; + val = this.getInputValue(); + hint = this.getHint(); + valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; + isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow(); + !isValid && this.clearHint(); + }, + hasFocus: function hasFocus() { + return this.$input.is(":focus"); + }, + hasOverflow: function hasOverflow() { + var constraint = this.$input.width() - 2; + this.$overflowHelper.text(this.getInputValue()); + return this.$overflowHelper.width() >= constraint; + }, + isCursorAtEnd: function() { + var valueLength, selectionStart, range; + valueLength = this.$input.val().length; + selectionStart = this.$input[0].selectionStart; + if (_.isNumber(selectionStart)) { + return selectionStart === valueLength; + } else if (document.selection) { + range = document.selection.createRange(); + range.moveStart("character", -valueLength); + return valueLength === range.text.length; + } + return true; + }, + destroy: function destroy() { + this.$hint.off(".tt"); + this.$input.off(".tt"); + this.$overflowHelper.remove(); + this.$hint = this.$input = this.$overflowHelper = $("
"); + }, + setAriaExpanded: function setAriaExpanded(value) { + this.$input.attr("aria-expanded", value); + } + }); + return Input; + function buildOverflowHelper($input) { + return $('').css({ + position: "absolute", + visibility: "hidden", + whiteSpace: "pre", + fontFamily: $input.css("font-family"), + fontSize: $input.css("font-size"), + fontStyle: $input.css("font-style"), + fontVariant: $input.css("font-variant"), + fontWeight: $input.css("font-weight"), + wordSpacing: $input.css("word-spacing"), + letterSpacing: $input.css("letter-spacing"), + textIndent: $input.css("text-indent"), + textRendering: $input.css("text-rendering"), + textTransform: $input.css("text-transform") + }).insertAfter($input); + } + function areQueriesEquivalent(a, b) { + return Input.normalizeQuery(a) === Input.normalizeQuery(b); + } + function withModifier($e) { + return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; + } + }(); + var Dataset = function() { + "use strict"; + var keys, nameGenerator; + keys = { + dataset: "tt-selectable-dataset", + val: "tt-selectable-display", + obj: "tt-selectable-object" + }; + nameGenerator = _.getIdGenerator(); + function Dataset(o, www) { + o = o || {}; + o.templates = o.templates || {}; + o.templates.notFound = o.templates.notFound || o.templates.empty; + if (!o.source) { + $.error("missing source"); + } + if (!o.node) { + $.error("missing node"); + } + if (o.name && !isValidName(o.name)) { + $.error("invalid dataset name: " + o.name); + } + www.mixin(this); + this.highlight = !!o.highlight; + this.name = _.toStr(o.name || nameGenerator()); + this.limit = o.limit || 5; + this.displayFn = getDisplayFn(o.display || o.displayKey); + this.templates = getTemplates(o.templates, this.displayFn); + this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source; + this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async; + this._resetLastSuggestion(); + this.$el = $(o.node).attr("role", "presentation").addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); + } + Dataset.extractData = function extractData(el) { + var $el = $(el); + if ($el.data(keys.obj)) { + return { + dataset: $el.data(keys.dataset) || "", + val: $el.data(keys.val) || "", + obj: $el.data(keys.obj) || null + }; + } + return null; + }; + _.mixin(Dataset.prototype, EventEmitter, { + _overwrite: function overwrite(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (this.async && this.templates.pending) { + this._renderPending(query); + } else if (!this.async && this.templates.notFound) { + this._renderNotFound(query); + } else { + this._empty(); + } + this.trigger("rendered", suggestions, false, this.name); + }, + _append: function append(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length && this.$lastSuggestion.length) { + this._appendSuggestions(query, suggestions); + } else if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (!this.$lastSuggestion.length && this.templates.notFound) { + this._renderNotFound(query); + } + this.trigger("rendered", suggestions, true, this.name); + }, + _renderSuggestions: function renderSuggestions(query, suggestions) { + var $fragment; + $fragment = this._getSuggestionsFragment(query, suggestions); + this.$lastSuggestion = $fragment.children().last(); + this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions)); + }, + _appendSuggestions: function appendSuggestions(query, suggestions) { + var $fragment, $lastSuggestion; + $fragment = this._getSuggestionsFragment(query, suggestions); + $lastSuggestion = $fragment.children().last(); + this.$lastSuggestion.after($fragment); + this.$lastSuggestion = $lastSuggestion; + }, + _renderPending: function renderPending(query) { + var template = this.templates.pending; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _renderNotFound: function renderNotFound(query) { + var template = this.templates.notFound; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _empty: function empty() { + this.$el.empty(); + this._resetLastSuggestion(); + }, + _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) { + var that = this, fragment; + fragment = document.createDocumentFragment(); + _.each(suggestions, function getSuggestionNode(suggestion) { + var $el, context; + context = that._injectQuery(query, suggestion); + $el = $(that.templates.suggestion(context)).data(keys.dataset, that.name).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable); + fragment.appendChild($el[0]); + }); + this.highlight && highlight({ + className: this.classes.highlight, + node: fragment, + pattern: query + }); + return $(fragment); + }, + _getFooter: function getFooter(query, suggestions) { + return this.templates.footer ? this.templates.footer({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _getHeader: function getHeader(query, suggestions) { + return this.templates.header ? this.templates.header({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _resetLastSuggestion: function resetLastSuggestion() { + this.$lastSuggestion = $(); + }, + _injectQuery: function injectQuery(query, obj) { + return _.isObject(obj) ? _.mixin({ + _query: query + }, obj) : obj; + }, + update: function update(query) { + var that = this, canceled = false, syncCalled = false, rendered = 0; + this.cancel(); + this.cancel = function cancel() { + canceled = true; + that.cancel = $.noop; + that.async && that.trigger("asyncCanceled", query, that.name); + }; + this.source(query, sync, async); + !syncCalled && sync([]); + function sync(suggestions) { + if (syncCalled) { + return; + } + syncCalled = true; + suggestions = (suggestions || []).slice(0, that.limit); + rendered = suggestions.length; + that._overwrite(query, suggestions); + if (rendered < that.limit && that.async) { + that.trigger("asyncRequested", query, that.name); + } + } + function async(suggestions) { + suggestions = suggestions || []; + if (!canceled && rendered < that.limit) { + that.cancel = $.noop; + var idx = Math.abs(rendered - that.limit); + rendered += idx; + that._append(query, suggestions.slice(0, idx)); + that.async && that.trigger("asyncReceived", query, that.name); + } + } + }, + cancel: $.noop, + clear: function clear() { + this._empty(); + this.cancel(); + this.trigger("cleared"); + }, + isEmpty: function isEmpty() { + return this.$el.is(":empty"); + }, + destroy: function destroy() { + this.$el = $("
"); + } + }); + return Dataset; + function getDisplayFn(display) { + display = display || _.stringify; + return _.isFunction(display) ? display : displayFn; + function displayFn(obj) { + return obj[display]; + } + } + function getTemplates(templates, displayFn) { + return { + notFound: templates.notFound && _.templatify(templates.notFound), + pending: templates.pending && _.templatify(templates.pending), + header: templates.header && _.templatify(templates.header), + footer: templates.footer && _.templatify(templates.footer), + suggestion: templates.suggestion ? userSuggestionTemplate : suggestionTemplate + }; + function userSuggestionTemplate(context) { + var template = templates.suggestion; + return $(template(context)).attr("id", _.guid()); + } + function suggestionTemplate(context) { + return $('
').attr("id", _.guid()).text(displayFn(context)); + } + } + function isValidName(str) { + return /^[_a-zA-Z0-9-]+$/.test(str); + } + }(); + var Menu = function() { + "use strict"; + function Menu(o, www) { + var that = this; + o = o || {}; + if (!o.node) { + $.error("node is required"); + } + www.mixin(this); + this.$node = $(o.node); + this.query = null; + this.datasets = _.map(o.datasets, initializeDataset); + function initializeDataset(oDataset) { + var node = that.$node.find(oDataset.node).first(); + oDataset.node = node.length ? node : $("
").appendTo(that.$node); + return new Dataset(oDataset, www); + } + } + _.mixin(Menu.prototype, EventEmitter, { + _onSelectableClick: function onSelectableClick($e) { + this.trigger("selectableClicked", $($e.currentTarget)); + }, + _onRendered: function onRendered(type, dataset, suggestions, async) { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetRendered", dataset, suggestions, async); + }, + _onCleared: function onCleared() { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetCleared"); + }, + _propagate: function propagate() { + this.trigger.apply(this, arguments); + }, + _allDatasetsEmpty: function allDatasetsEmpty() { + return _.every(this.datasets, _.bind(function isDatasetEmpty(dataset) { + var isEmpty = dataset.isEmpty(); + this.$node.attr("aria-expanded", !isEmpty); + return isEmpty; + }, this)); + }, + _getSelectables: function getSelectables() { + return this.$node.find(this.selectors.selectable); + }, + _removeCursor: function _removeCursor() { + var $selectable = this.getActiveSelectable(); + $selectable && $selectable.removeClass(this.classes.cursor); + }, + _ensureVisible: function ensureVisible($el) { + var elTop, elBottom, nodeScrollTop, nodeHeight; + elTop = $el.position().top; + elBottom = elTop + $el.outerHeight(true); + nodeScrollTop = this.$node.scrollTop(); + nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10); + if (elTop < 0) { + this.$node.scrollTop(nodeScrollTop + elTop); + } else if (nodeHeight < elBottom) { + this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight)); + } + }, + bind: function() { + var that = this, onSelectableClick; + onSelectableClick = _.bind(this._onSelectableClick, this); + this.$node.on("click.tt", this.selectors.selectable, onSelectableClick); + this.$node.on("mouseover", this.selectors.selectable, function() { + that.setCursor($(this)); + }); + this.$node.on("mouseleave", function() { + that._removeCursor(); + }); + _.each(this.datasets, function(dataset) { + dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that); + }); + return this; + }, + isOpen: function isOpen() { + return this.$node.hasClass(this.classes.open); + }, + open: function open() { + this.$node.scrollTop(0); + this.$node.addClass(this.classes.open); + }, + close: function close() { + this.$node.attr("aria-expanded", false); + this.$node.removeClass(this.classes.open); + this._removeCursor(); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.attr("dir", dir); + }, + selectableRelativeToCursor: function selectableRelativeToCursor(delta) { + var $selectables, $oldCursor, oldIndex, newIndex; + $oldCursor = this.getActiveSelectable(); + $selectables = this._getSelectables(); + oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1; + newIndex = oldIndex + delta; + newIndex = (newIndex + 1) % ($selectables.length + 1) - 1; + newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex; + return newIndex === -1 ? null : $selectables.eq(newIndex); + }, + setCursor: function setCursor($selectable) { + this._removeCursor(); + if ($selectable = $selectable && $selectable.first()) { + $selectable.addClass(this.classes.cursor); + this._ensureVisible($selectable); + } + }, + getSelectableData: function getSelectableData($el) { + return $el && $el.length ? Dataset.extractData($el) : null; + }, + getActiveSelectable: function getActiveSelectable() { + var $selectable = this._getSelectables().filter(this.selectors.cursor).first(); + return $selectable.length ? $selectable : null; + }, + getTopSelectable: function getTopSelectable() { + var $selectable = this._getSelectables().first(); + return $selectable.length ? $selectable : null; + }, + update: function update(query) { + var isValidUpdate = query !== this.query; + if (isValidUpdate) { + this.query = query; + _.each(this.datasets, updateDataset); + } + return isValidUpdate; + function updateDataset(dataset) { + dataset.update(query); + } + }, + empty: function empty() { + _.each(this.datasets, clearDataset); + this.query = null; + this.$node.addClass(this.classes.empty); + function clearDataset(dataset) { + dataset.clear(); + } + }, + destroy: function destroy() { + this.$node.off(".tt"); + this.$node = $("
"); + _.each(this.datasets, destroyDataset); + function destroyDataset(dataset) { + dataset.destroy(); + } + } + }); + return Menu; + }(); + var Status = function() { + "use strict"; + function Status(options) { + this.$el = $("", { + role: "status", + "aria-live": "polite" + }).css({ + position: "absolute", + padding: "0", + border: "0", + height: "1px", + width: "1px", + "margin-bottom": "-1px", + "margin-right": "-1px", + overflow: "hidden", + clip: "rect(0 0 0 0)", + "white-space": "nowrap" + }); + options.$input.after(this.$el); + _.each(options.menu.datasets, _.bind(function(dataset) { + if (dataset.onSync) { + dataset.onSync("rendered", _.bind(this.update, this)); + dataset.onSync("cleared", _.bind(this.cleared, this)); + } + }, this)); + } + _.mixin(Status.prototype, { + update: function update(event, suggestions) { + var length = suggestions.length; + var words; + if (length === 1) { + words = { + result: "result", + is: "is" + }; + } else { + words = { + result: "results", + is: "are" + }; + } + this.$el.text(length + " " + words.result + " " + words.is + " available, use up and down arrow keys to navigate."); + }, + cleared: function() { + this.$el.text(""); + } + }); + return Status; + }(); + var DefaultMenu = function() { + "use strict"; + var s = Menu.prototype; + function DefaultMenu() { + Menu.apply(this, [].slice.call(arguments, 0)); + } + _.mixin(DefaultMenu.prototype, Menu.prototype, { + open: function open() { + !this._allDatasetsEmpty() && this._show(); + return s.open.apply(this, [].slice.call(arguments, 0)); + }, + close: function close() { + this._hide(); + return s.close.apply(this, [].slice.call(arguments, 0)); + }, + _onRendered: function onRendered() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onRendered.apply(this, [].slice.call(arguments, 0)); + }, + _onCleared: function onCleared() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onCleared.apply(this, [].slice.call(arguments, 0)); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl); + return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0)); + }, + _hide: function hide() { + this.$node.hide(); + }, + _show: function show() { + this.$node.css("display", "block"); + } + }); + return DefaultMenu; + }(); + var Typeahead = function() { + "use strict"; + function Typeahead(o, www) { + var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged; + o = o || {}; + if (!o.input) { + $.error("missing input"); + } + if (!o.menu) { + $.error("missing menu"); + } + if (!o.eventBus) { + $.error("missing event bus"); + } + www.mixin(this); + this.eventBus = o.eventBus; + this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; + this.input = o.input; + this.menu = o.menu; + this.enabled = true; + this.autoselect = !!o.autoselect; + this.active = false; + this.input.hasFocus() && this.activate(); + this.dir = this.input.getLangDir(); + this._hacks(); + this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this); + onFocused = c(this, "activate", "open", "_onFocused"); + onBlurred = c(this, "deactivate", "_onBlurred"); + onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed"); + onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed"); + onEscKeyed = c(this, "isActive", "_onEscKeyed"); + onUpKeyed = c(this, "isActive", "open", "_onUpKeyed"); + onDownKeyed = c(this, "isActive", "open", "_onDownKeyed"); + onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed"); + onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed"); + onQueryChanged = c(this, "_openIfActive", "_onQueryChanged"); + onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged"); + this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this); + } + _.mixin(Typeahead.prototype, { + _hacks: function hacks() { + var $input, $menu; + $input = this.input.$input || $("
"); + $menu = this.menu.$node || $("
"); + $input.on("blur.tt", function($e) { + var active, isActive, hasActive; + active = document.activeElement; + isActive = $menu.is(active); + hasActive = $menu.has(active).length > 0; + if (_.isMsie() && (isActive || hasActive)) { + $e.preventDefault(); + $e.stopImmediatePropagation(); + _.defer(function() { + $input.focus(); + }); + } + }); + $menu.on("mousedown.tt", function($e) { + $e.preventDefault(); + }); + }, + _onSelectableClicked: function onSelectableClicked(type, $el) { + this.select($el); + }, + _onDatasetCleared: function onDatasetCleared() { + this._updateHint(); + }, + _onDatasetRendered: function onDatasetRendered(type, suggestions, async, dataset) { + this._updateHint(); + if (this.autoselect) { + var cursorClass = this.selectors.cursor.substr(1); + this.menu.$node.find(this.selectors.suggestion).first().addClass(cursorClass); + } + this.eventBus.trigger("render", suggestions, async, dataset); + }, + _onAsyncRequested: function onAsyncRequested(type, dataset, query) { + this.eventBus.trigger("asyncrequest", query, dataset); + }, + _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) { + this.eventBus.trigger("asynccancel", query, dataset); + }, + _onAsyncReceived: function onAsyncReceived(type, dataset, query) { + this.eventBus.trigger("asyncreceive", query, dataset); + }, + _onFocused: function onFocused() { + this._minLengthMet() && this.menu.update(this.input.getQuery()); + }, + _onBlurred: function onBlurred() { + if (this.input.hasQueryChangedSinceLastFocus()) { + this.eventBus.trigger("change", this.input.getQuery()); + } + }, + _onEnterKeyed: function onEnterKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + if (this.select($selectable)) { + $e.preventDefault(); + $e.stopPropagation(); + } + } else if (this.autoselect) { + if (this.select(this.menu.getTopSelectable())) { + $e.preventDefault(); + $e.stopPropagation(); + } + } + }, + _onTabKeyed: function onTabKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + this.select($selectable) && $e.preventDefault(); + } else if (this.autoselect) { + if ($selectable = this.menu.getTopSelectable()) { + this.autocomplete($selectable) && $e.preventDefault(); + } + } + }, + _onEscKeyed: function onEscKeyed() { + this.close(); + }, + _onUpKeyed: function onUpKeyed() { + this.moveCursor(-1); + }, + _onDownKeyed: function onDownKeyed() { + this.moveCursor(+1); + }, + _onLeftKeyed: function onLeftKeyed() { + if (this.dir === "rtl" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); + } + }, + _onRightKeyed: function onRightKeyed() { + if (this.dir === "ltr" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); + } + }, + _onQueryChanged: function onQueryChanged(e, query) { + this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty(); + }, + _onWhitespaceChanged: function onWhitespaceChanged() { + this._updateHint(); + }, + _onLangDirChanged: function onLangDirChanged(e, dir) { + if (this.dir !== dir) { + this.dir = dir; + this.menu.setLanguageDirection(dir); + } + }, + _openIfActive: function openIfActive() { + this.isActive() && this.open(); + }, + _minLengthMet: function minLengthMet(query) { + query = _.isString(query) ? query : this.input.getQuery() || ""; + return query.length >= this.minLength; + }, + _updateHint: function updateHint() { + var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match; + $selectable = this.menu.getTopSelectable(); + data = this.menu.getSelectableData($selectable); + val = this.input.getInputValue(); + if (data && !_.isBlankString(val) && !this.input.hasOverflow()) { + query = Input.normalizeQuery(val); + escapedQuery = _.escapeRegExChars(query); + frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i"); + match = frontMatchRegEx.exec(data.val); + match && this.input.setHint(val + match[1]); + } else { + this.input.clearHint(); + } + }, + isEnabled: function isEnabled() { + return this.enabled; + }, + enable: function enable() { + this.enabled = true; + }, + disable: function disable() { + this.enabled = false; + }, + isActive: function isActive() { + return this.active; + }, + activate: function activate() { + if (this.isActive()) { + return true; + } else if (!this.isEnabled() || this.eventBus.before("active")) { + return false; + } else { + this.active = true; + this.eventBus.trigger("active"); + return true; + } + }, + deactivate: function deactivate() { + if (!this.isActive()) { + return true; + } else if (this.eventBus.before("idle")) { + return false; + } else { + this.active = false; + this.close(); + this.eventBus.trigger("idle"); + return true; + } + }, + isOpen: function isOpen() { + return this.menu.isOpen(); + }, + open: function open() { + if (!this.isOpen() && !this.eventBus.before("open")) { + this.input.setAriaExpanded(true); + this.menu.open(); + this._updateHint(); + this.eventBus.trigger("open"); + } + return this.isOpen(); + }, + close: function close() { + if (this.isOpen() && !this.eventBus.before("close")) { + this.input.setAriaExpanded(false); + this.menu.close(); + this.input.clearHint(); + this.input.resetInputValue(); + this.eventBus.trigger("close"); + } + return !this.isOpen(); + }, + setVal: function setVal(val) { + this.input.setQuery(_.toStr(val)); + }, + getVal: function getVal() { + return this.input.getQuery(); + }, + select: function select($selectable) { + var data = this.menu.getSelectableData($selectable); + if (data && !this.eventBus.before("select", data.obj, data.dataset)) { + this.input.setQuery(data.val, true); + this.eventBus.trigger("select", data.obj, data.dataset); + this.close(); + return true; + } + return false; + }, + autocomplete: function autocomplete($selectable) { + var query, data, isValid; + query = this.input.getQuery(); + data = this.menu.getSelectableData($selectable); + isValid = data && query !== data.val; + if (isValid && !this.eventBus.before("autocomplete", data.obj, data.dataset)) { + this.input.setQuery(data.val); + this.eventBus.trigger("autocomplete", data.obj, data.dataset); + return true; + } + return false; + }, + moveCursor: function moveCursor(delta) { + var query, $candidate, data, suggestion, datasetName, cancelMove, id; + query = this.input.getQuery(); + $candidate = this.menu.selectableRelativeToCursor(delta); + data = this.menu.getSelectableData($candidate); + suggestion = data ? data.obj : null; + datasetName = data ? data.dataset : null; + id = $candidate ? $candidate.attr("id") : null; + this.input.trigger("cursorchange", id); + cancelMove = this._minLengthMet() && this.menu.update(query); + if (!cancelMove && !this.eventBus.before("cursorchange", suggestion, datasetName)) { + this.menu.setCursor($candidate); + if (data) { + if (typeof data.val === "string") { + this.input.setInputValue(data.val); + } + } else { + this.input.resetInputValue(); + this._updateHint(); + } + this.eventBus.trigger("cursorchange", suggestion, datasetName); + return true; + } + return false; + }, + destroy: function destroy() { + this.input.destroy(); + this.menu.destroy(); + } + }); + return Typeahead; + function c(ctx) { + var methods = [].slice.call(arguments, 1); + return function() { + var args = [].slice.call(arguments); + _.each(methods, function(method) { + return ctx[method].apply(ctx, args); + }); + }; + } + }(); + (function() { + "use strict"; + var old, keys, methods; + old = $.fn.typeahead; + keys = { + www: "tt-www", + attrs: "tt-attrs", + typeahead: "tt-typeahead" + }; + methods = { + initialize: function initialize(o, datasets) { + var www; + datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); + o = o || {}; + www = WWW(o.classNames); + return this.each(attach); + function attach() { + var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, status, typeahead, MenuConstructor; + _.each(datasets, function(d) { + d.highlight = !!o.highlight; + }); + $input = $(this); + $wrapper = $(www.html.wrapper); + $hint = $elOrNull(o.hint); + $menu = $elOrNull(o.menu); + defaultHint = o.hint !== false && !$hint; + defaultMenu = o.menu !== false && !$menu; + defaultHint && ($hint = buildHintFromInput($input, www)); + defaultMenu && ($menu = $(www.html.menu).css(www.css.menu)); + $hint && $hint.val(""); + $input = prepInput($input, www); + if (defaultHint || defaultMenu) { + $wrapper.css(www.css.wrapper); + $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint); + $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null); + } + MenuConstructor = defaultMenu ? DefaultMenu : Menu; + eventBus = new EventBus({ + el: $input + }); + input = new Input({ + hint: $hint, + input: $input, + menu: $menu + }, www); + menu = new MenuConstructor({ + node: $menu, + datasets: datasets + }, www); + status = new Status({ + $input: $input, + menu: menu + }); + typeahead = new Typeahead({ + input: input, + menu: menu, + eventBus: eventBus, + minLength: o.minLength, + autoselect: o.autoselect + }, www); + $input.data(keys.www, www); + $input.data(keys.typeahead, typeahead); + } + }, + isEnabled: function isEnabled() { + var enabled; + ttEach(this.first(), function(t) { + enabled = t.isEnabled(); + }); + return enabled; + }, + enable: function enable() { + ttEach(this, function(t) { + t.enable(); + }); + return this; + }, + disable: function disable() { + ttEach(this, function(t) { + t.disable(); + }); + return this; + }, + isActive: function isActive() { + var active; + ttEach(this.first(), function(t) { + active = t.isActive(); + }); + return active; + }, + activate: function activate() { + ttEach(this, function(t) { + t.activate(); + }); + return this; + }, + deactivate: function deactivate() { + ttEach(this, function(t) { + t.deactivate(); + }); + return this; + }, + isOpen: function isOpen() { + var open; + ttEach(this.first(), function(t) { + open = t.isOpen(); + }); + return open; + }, + open: function open() { + ttEach(this, function(t) { + t.open(); + }); + return this; + }, + close: function close() { + ttEach(this, function(t) { + t.close(); + }); + return this; + }, + select: function select(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.select($el); + }); + return success; + }, + autocomplete: function autocomplete(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.autocomplete($el); + }); + return success; + }, + moveCursor: function moveCursoe(delta) { + var success = false; + ttEach(this.first(), function(t) { + success = t.moveCursor(delta); + }); + return success; + }, + val: function val(newVal) { + var query; + if (!arguments.length) { + ttEach(this.first(), function(t) { + query = t.getVal(); + }); + return query; + } else { + ttEach(this, function(t) { + t.setVal(_.toStr(newVal)); + }); + return this; + } + }, + destroy: function destroy() { + ttEach(this, function(typeahead, $input) { + revert($input); + typeahead.destroy(); + }); + return this; + } + }; + $.fn.typeahead = function(method) { + if (methods[method]) { + return methods[method].apply(this, [].slice.call(arguments, 1)); + } else { + return methods.initialize.apply(this, arguments); + } + }; + $.fn.typeahead.noConflict = function noConflict() { + $.fn.typeahead = old; + return this; + }; + function ttEach($els, fn) { + $els.each(function() { + var $input = $(this), typeahead; + (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input); + }); + } + function buildHintFromInput($input, www) { + return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop({ + readonly: true, + required: false + }).removeAttr("id name placeholder").removeClass("required").attr({ + spellcheck: "false", + tabindex: -1 + }); + } + function prepInput($input, www) { + $input.data(keys.attrs, { + dir: $input.attr("dir"), + autocomplete: $input.attr("autocomplete"), + spellcheck: $input.attr("spellcheck"), + style: $input.attr("style") + }); + $input.addClass(www.classes.input).attr({ + spellcheck: false + }); + try { + !$input.attr("dir") && $input.attr("dir", "auto"); + } catch (e) {} + return $input; + } + function getBackgroundStyles($el) { + return { + backgroundAttachment: $el.css("background-attachment"), + backgroundClip: $el.css("background-clip"), + backgroundColor: $el.css("background-color"), + backgroundImage: $el.css("background-image"), + backgroundOrigin: $el.css("background-origin"), + backgroundPosition: $el.css("background-position"), + backgroundRepeat: $el.css("background-repeat"), + backgroundSize: $el.css("background-size") + }; + } + function revert($input) { + var www, $wrapper; + www = $input.data(keys.www); + $wrapper = $input.parent().filter(www.selectors.wrapper); + _.each($input.data(keys.attrs), function(val, key) { + _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); + }); + $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input); + if ($wrapper.length) { + $input.detach().insertAfter($wrapper); + $wrapper.remove(); + } + } + function $elOrNull(obj) { + var isValid, $el; + isValid = _.isJQuery(obj) || _.isElement(obj); + $el = isValid ? $(obj).first() : []; + return $el.length ? $el : null; + } + })(); +}); \ No newline at end of file diff --git a/docs/1.9.1/AsyncHTTPClient/search.json b/docs/1.9.1/AsyncHTTPClient/search.json new file mode 100644 index 000000000..f2268def2 --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/search.json @@ -0,0 +1 @@ +{"Structs/HTTPClientError.html#/s:s23CustomStringConvertibleP11descriptionSSvp":{"name":"description","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV10invalidURLACvpZ":{"name":"invalidURL","abstract":"

URL provided is invalid.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV9emptyHostACvpZ":{"name":"emptyHost","abstract":"

URL does not contain host.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV17missingSocketPathACvpZ":{"name":"missingSocketPath","abstract":"

URL does not contain a socketPath as a host for http(s)+unix shemes.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV15alreadyShutdownACvpZ":{"name":"alreadyShutdown","abstract":"

Client is shutdown and cannot be used for new requests.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV11emptySchemeACvpZ":{"name":"emptyScheme","abstract":"

URL does not contain scheme.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV17unsupportedSchemeyACSSFZ":{"name":"unsupportedScheme(_:)","abstract":"

Provided URL scheme is not supported, supported schemes are: http and https

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV11readTimeoutACvpZ":{"name":"readTimeout","abstract":"

Request timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV22remoteConnectionClosedACvpZ":{"name":"remoteConnectionClosed","abstract":"

Remote connection was closed unexpectedly.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV9cancelledACvpZ":{"name":"cancelled","abstract":"

Request was cancelled.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV32identityCodingIncorrectlyPresentACvpZ":{"name":"identityCodingIncorrectlyPresent","abstract":"

Request contains invalid identity encoding.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV29chunkedSpecifiedMultipleTimesACvpZ":{"name":"chunkedSpecifiedMultipleTimes","abstract":"

Request contains multiple chunks definitions.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20invalidProxyResponseACvpZ":{"name":"invalidProxyResponse","abstract":"

Proxy response was invalid.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20contentLengthMissingACvpZ":{"name":"contentLengthMissing","abstract":"

Request does not contain Content-Length header.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV27proxyAuthenticationRequiredACvpZ":{"name":"proxyAuthenticationRequired","abstract":"

Proxy Authentication Required.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20redirectLimitReachedACvpZ":{"name":"redirectLimitReached","abstract":"

Redirect Limit reached.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV21redirectCycleDetectedACvpZ":{"name":"redirectCycleDetected","abstract":"

Redirect Cycle detected.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV15uncleanShutdownACvpZ":{"name":"uncleanShutdown","abstract":"

Unclean shutdown.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20traceRequestWithBodyACvpZ":{"name":"traceRequestWithBody","abstract":"

A body was sent in a request with method TRACE.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV23invalidHeaderFieldNamesyACSaySSGFZ":{"name":"invalidHeaderFieldNames(_:)","abstract":"

Header field names contain invalid characters.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV24invalidHeaderFieldValuesyACSaySSGFZ":{"name":"invalidHeaderFieldValues(_:)","abstract":"

Header field values contain invalid characters.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV18bodyLengthMismatchACvpZ":{"name":"bodyLengthMismatch","abstract":"

Body length is not equal to Content-Length.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV21writeAfterRequestSentACvpZ":{"name":"writeAfterRequestSent","abstract":"

Body part was written after request was fully sent.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV19incompatibleHeadersACvpZ":{"name":"incompatibleHeaders","abstract":"

Incompatible headers specified, for example Transfer-Encoding and Content-Length.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV14connectTimeoutACvpZ":{"name":"connectTimeout","abstract":"

Creating a new tcp connection timed out

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV21socksHandshakeTimeoutACvpZ":{"name":"socksHandshakeTimeout","abstract":"

The socks handshake timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV25httpProxyHandshakeTimeoutACvpZ":{"name":"httpProxyHandshakeTimeout","abstract":"

The http proxy connection creation timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV19tlsHandshakeTimeoutACvpZ":{"name":"tlsHandshakeTimeout","abstract":"

The tls handshake timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV43serverOfferedUnsupportedApplicationProtocolyACSSFZ":{"name":"serverOfferedUnsupportedApplicationProtocol(_:)","abstract":"

The remote server only offered an unsupported application protocol

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV16deadlineExceededACvpZ":{"name":"deadlineExceeded","abstract":"

The request deadline was exceeded. The request was cancelled because of this.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV22requestStreamCancelledACvpZ":{"name":"requestStreamCancelled","abstract":"

The remote server responded with a status code >= 300, before the full request was sent. The request stream","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV28getConnectionFromPoolTimeoutACvpZ":{"name":"getConnectionFromPoolTimeout","abstract":"

Aquiring a HTTP connection from the connection pool timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV31httpEndReceivedAfterHeadWith1xxACvpZ":{"name":"httpEndReceivedAfterHeadWith1xx","abstract":"

Undocumented

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html":{"name":"HTTPClientError","abstract":"

Possible client errors.

"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP0C0Qa":{"name":"Response","abstract":"

Undocumented

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didSendRequestHead4task_yAA0B0C4TaskCy_0C0QzG_8NIOHTTP1011HTTPRequestH0VtF":{"name":"didSendRequestHead(task:_:)","abstract":"

Called when the request head is sent. Will be called once.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didSendRequestPart4task_yAA0B0C4TaskCy_0C0QzG_7NIOCore6IODataOtF":{"name":"didSendRequestPart(task:_:)","abstract":"

Called when a part of the request body is sent. Could be called zero or more times.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didSendRequest4taskyAA0B0C4TaskCy_0C0QzG_tF":{"name":"didSendRequest(task:)","abstract":"

Called when the request is fully sent. Will be called once.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","abstract":"

Called when response head is received. Will be called once.","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","abstract":"

Called when part of a response body is received. Could be called zero or more times.","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","abstract":"

Called when error was thrown during request execution. Will be called zero or one time only. Request processing will be stopped after that.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","abstract":"

Called when the complete HTTP request is finished. You must return an instance of your Response associated type. Will be called once, except if an error occurred.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html":{"name":"HTTPClientResponseDelegate","abstract":"

HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed."},"Extensions/URL.html#/s:10Foundation3URLV15AsyncHTTPClientE21httpURLWithSocketPath3uriACSgSS_SStcfc":{"name":"init(httpURLWithSocketPath:uri:)","abstract":"

Initializes a newly created HTTP URL connecting to a unix domain socket path. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “http+unix” scheme.

","parent_name":"URL"},"Extensions/URL.html#/s:10Foundation3URLV15AsyncHTTPClientE22httpsURLWithSocketPath3uriACSgSS_SStcfc":{"name":"init(httpsURLWithSocketPath:uri:)","abstract":"

Initializes a newly created HTTPS URL connecting to a unix domain socket path over TLS. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “https+unix” scheme.

","parent_name":"URL"},"Extensions/URL.html":{"name":"URL"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B15CopyingDelegateC8Responsea":{"name":"Response","abstract":"

Undocumented

","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B15CopyingDelegateC12chunkHandlerAC7NIOCore15EventLoopFutureCyytGAE10ByteBufferVc_tcfc":{"name":"init(chunkHandler:)","abstract":"

Undocumented

","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"HTTPClientCopyingDelegate"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient19ResponseAccumulatorC0C0a":{"name":"Response","abstract":"

Undocumented

","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient19ResponseAccumulatorC7requestAcA0B0C7RequestV_tcfc":{"name":"init(request:)","abstract":"

Undocumented

","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"ResponseAccumulator"},"Classes/HTTPClient/NWTLSError.html#/status":{"name":"status","abstract":"

TLS error status. List of TLS errors can be found in

","parent_name":"NWTLSError"},"Classes/HTTPClient/NWTLSError.html#/init(_:reason:)":{"name":"init(_:reason:)","abstract":"

initialise a NWTLSError

","parent_name":"NWTLSError"},"Classes/HTTPClient/NWTLSError.html#/description":{"name":"description","parent_name":"NWTLSError"},"Classes/HTTPClient/NWPOSIXError.html#/errorCode":{"name":"errorCode","abstract":"

POSIX error code (enum)

","parent_name":"NWPOSIXError"},"Classes/HTTPClient/NWPOSIXError.html#/init(_:reason:)":{"name":"init(_:reason:)","abstract":"

Initialise a NWPOSIXError

","parent_name":"NWPOSIXError"},"Classes/HTTPClient/NWPOSIXError.html#/description":{"name":"description","parent_name":"NWPOSIXError"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC9eventLoop7NIOCore05EventE0_pvp":{"name":"eventLoop","abstract":"

The EventLoop the delegate will be executed on.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC12futureResult7NIOCore15EventLoopFutureCyxGvp":{"name":"futureResult","abstract":"

EventLoopFuture for the response returned by this request.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC4waitxyKF":{"name":"wait()","abstract":"

Waits for execution of this request to complete.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC6cancelyyF":{"name":"cancel()","abstract":"

Cancels the request execution.

","parent_name":"Task"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV5basic8username8passwordAESS_SStFZ":{"name":"basic(username:password:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV5basic11credentialsAESS_tFZ":{"name":"basic(credentials:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV6bearer6tokensAESS_tFZ":{"name":"bearer(tokens:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV11headerValueSSvp":{"name":"headerValue","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6method8NIOHTTP110HTTPMethodOvp":{"name":"method","abstract":"

Request HTTP method, defaults to GET.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url10Foundation3URLVvp":{"name":"url","abstract":"

Remote URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6schemeSSvp":{"name":"scheme","abstract":"

Remote HTTP scheme, resolved from URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV7headers8NIOHTTP111HTTPHeadersVvp":{"name":"headers","abstract":"

Request custom HTTP Headers, defaults to no headers.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4bodyAC4BodyVSgvp":{"name":"body","abstract":"

Request body, defaults to no body.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV16tlsConfiguration6NIOSSL16TLSConfigurationVSgvp":{"name":"tlsConfiguration","abstract":"

Request-specific TLS configuration, defaults to no request-specific TLS configuration.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4bodyAESS_8NIOHTTP110HTTPMethodOAJ11HTTPHeadersVAC4BodyVSgtKcfc":{"name":"init(url:method:headers:body:)","abstract":"

Create HTTP request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4body16tlsConfigurationAESS_8NIOHTTP110HTTPMethodOAK11HTTPHeadersVAC4BodyVSg6NIOSSL16TLSConfigurationVSgtKcfc":{"name":"init(url:method:headers:body:tlsConfiguration:)","abstract":"

Create HTTP request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4bodyAE10Foundation3URLV_8NIOHTTP110HTTPMethodOAM11HTTPHeadersVAC4BodyVSgtKcfc":{"name":"init(url:method:headers:body:)","abstract":"

Create an HTTP Request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4body16tlsConfigurationAE10Foundation3URLV_8NIOHTTP110HTTPMethodOAN11HTTPHeadersVAC4BodyVSg6NIOSSL16TLSConfigurationVSgtKcfc":{"name":"init(url:method:headers:body:tlsConfiguration:)","abstract":"

Create an HTTP Request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4hostSSvp":{"name":"host","abstract":"

Remote host, resolved from URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4portSivp":{"name":"port","abstract":"

Resolved port.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6useTLSSbvp":{"name":"useTLS","abstract":"

Whether request will be executed using secure socket.

","parent_name":"Request"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4hostSSvp":{"name":"host","abstract":"

Remote host of the request.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV6status8NIOHTTP118HTTPResponseStatusOvp":{"name":"status","abstract":"

Response HTTP status.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7version8NIOHTTP111HTTPVersionVvp":{"name":"version","abstract":"

Response HTTP version.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7headers8NIOHTTP111HTTPHeadersVvp":{"name":"headers","abstract":"

Reponse HTTP headers.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4body7NIOCore10ByteBufferVSgvp":{"name":"body","abstract":"

Response body.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4host6status7headers4bodyAESS_8NIOHTTP118HTTPResponseStatusOAJ11HTTPHeadersV7NIOCore10ByteBufferVSgtcfc":{"name":"init(host:status:headers:body:)","abstract":"

Create HTTP Response.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4host6status7version7headers4bodyAESS_8NIOHTTP118HTTPResponseStatusOAK11HTTPVersionVAK11HTTPHeadersV7NIOCore10ByteBufferVSgtcfc":{"name":"init(host:status:version:headers:body:)","abstract":"

Create HTTP Response.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7cookiesSayAC6CookieVGvp":{"name":"cookies","abstract":"

List of HTTP cookies returned by the server.

","parent_name":"Response"},"Classes/HTTPClient/Body/StreamWriter.html#/s:15AsyncHTTPClient0B0C4BodyV12StreamWriterV7closureAG7NIOCore15EventLoopFutureCyytGAI6IODataOc_tcfc":{"name":"init(closure:)","abstract":"

Create new StreamWriter

","parent_name":"StreamWriter"},"Classes/HTTPClient/Body/StreamWriter.html#/s:15AsyncHTTPClient0B0C4BodyV12StreamWriterV5writey7NIOCore15EventLoopFutureCyytGAI6IODataOF":{"name":"write(_:)","abstract":"

Write data to server.

","parent_name":"StreamWriter"},"Classes/HTTPClient/Body/StreamWriter.html":{"name":"StreamWriter","abstract":"

Chunk provider.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6lengthSiSgvp":{"name":"length","abstract":"

Body size. if nil,Transfer-Encoding will automatically be set to chunked. Otherwise a Content-Length","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6streamy7NIOCore15EventLoopFutureCyytGAE12StreamWriterVcvp":{"name":"stream","abstract":"

Body chunk provider.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV10byteBufferyAE7NIOCore04ByteE0VFZ":{"name":"byteBuffer(_:)","abstract":"

Create and stream body using ByteBuffer.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6stream6length_AESiSg_7NIOCore15EventLoopFutureCyytGAE12StreamWriterVctFZ":{"name":"stream(length:_:)","abstract":"

Create and stream body using StreamWriter.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV5bytesyAExSkRzs5UInt8V7ElementRtzlFZ":{"name":"bytes(_:)","abstract":"

Create and stream body using a collection of bytes.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6stringyAESSFZ":{"name":"string(_:)","abstract":"

Create and stream body using String.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV4datayAE10Foundation4DataVFZ":{"name":"data(_:)","abstract":"

Create and stream body using Data.

","parent_name":"Body"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4nameSSvp":{"name":"name","abstract":"

The name of the cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV5valueSSvp":{"name":"value","abstract":"

The cookie’s string value.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4pathSSvp":{"name":"path","abstract":"

The cookie’s path.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6domainSSSgvp":{"name":"domain","abstract":"

The domain of the cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6maxAgeSiSgvp":{"name":"maxAge","abstract":"

The cookie’s age in seconds.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV8httpOnlySbvp":{"name":"httpOnly","abstract":"

Whether the cookie should only be sent to HTTP servers.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6secureSbvp":{"name":"secure","abstract":"

Whether the cookie should only be sent over secure channels.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6header13defaultDomainAESgSS_SStcfc":{"name":"init(header:defaultDomain:)","abstract":"

Create a Cookie by parsing a Set-Cookie header.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV7expires10Foundation4DateVSgvp":{"name":"expires","abstract":"

The cookie’s expiration date.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4name5value4path6domain7expires6maxAge8httpOnly6secureAESS_S3SSg10Foundation4DateVSgSiSgS2btcfc":{"name":"init(name:value:path:domain:expires:maxAge:httpOnly:secure:)","abstract":"

Create HTTP cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Decompression.html#/s:15AsyncHTTPClient0B0C13DecompressionO8disabledyA2EmF":{"name":"disabled","abstract":"

Decompression is disabled.

","parent_name":"Decompression"},"Classes/HTTPClient/Decompression.html#/s:15AsyncHTTPClient0B0C13DecompressionO7enabledyAE18NIOHTTPCompression20NIOHTTPDecompressionO0C5LimitV_tcAEmF":{"name":"enabled(limit:)","abstract":"

Decompression is enabled.

","parent_name":"Decompression"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV11indifferentAEvpZ":{"name":"indifferent","abstract":"

Event Loop will be selected by the library.

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV8delegate2onAE7NIOCore0cD0_p_tFZ":{"name":"delegate(on:)","abstract":"

The delegate will be run on the specified EventLoop (and the Channel if possible).

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV18delegateAndChannel2onAE7NIOCore0cD0_p_tFZ":{"name":"delegateAndChannel(on:)","abstract":"

The delegate and the Channel will be run on the specified EventLoop.

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:s23CustomStringConvertibleP11descriptionSSvp":{"name":"description","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopGroupProvider.html#/s:15AsyncHTTPClient0B0C22EventLoopGroupProviderO6sharedyAE7NIOCore0cdE0_pcAEmF":{"name":"shared(_:)","abstract":"

EventLoopGroup will be provided by the user. Owner of this group is responsible for its lifecycle.

","parent_name":"EventLoopGroupProvider"},"Classes/HTTPClient/EventLoopGroupProvider.html#/s:15AsyncHTTPClient0B0C22EventLoopGroupProviderO9createNewyA2EmF":{"name":"createNew","abstract":"

EventLoopGroup will be created by the client. When syncShutdown is called, created EventLoopGroup will be shut down as well.

","parent_name":"EventLoopGroupProvider"},"Classes/HTTPClient/Configuration/HTTPVersion.html#/s:15AsyncHTTPClient0B0C13ConfigurationV11HTTPVersionV9http1OnlyAGvpZ":{"name":"http1Only","abstract":"

we only use HTTP/1, even if the server would supports HTTP/2

","parent_name":"HTTPVersion"},"Classes/HTTPClient/Configuration/HTTPVersion.html#/s:15AsyncHTTPClient0B0C13ConfigurationV11HTTPVersionV9automaticAGvpZ":{"name":"automatic","abstract":"

HTTP/2 is used if we connect to a server with HTTPS and the server supports HTTP/2, otherwise we use HTTP/1

","parent_name":"HTTPVersion"},"Classes/HTTPClient/Configuration/ConnectionPool.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14ConnectionPoolV11idleTimeout7NIOCore10TimeAmountVvp":{"name":"idleTimeout","abstract":"

Specifies amount of time connections are kept idle in the pool. After this time has passed without a new","parent_name":"ConnectionPool"},"Classes/HTTPClient/Configuration/ConnectionPool.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14ConnectionPoolV42concurrentHTTP1ConnectionsPerHostSoftLimitSivp":{"name":"concurrentHTTP1ConnectionsPerHostSoftLimit","abstract":"

The maximum number of connections that are kept alive in the connection pool per host. If requests with","parent_name":"ConnectionPool"},"Classes/HTTPClient/Configuration/ConnectionPool.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14ConnectionPoolV11idleTimeoutAG7NIOCore10TimeAmountV_tcfc":{"name":"init(idleTimeout:)","abstract":"

Undocumented

","parent_name":"ConnectionPool"},"Classes/HTTPClient/Configuration/ConnectionPool.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14ConnectionPoolV11idleTimeout42concurrentHTTP1ConnectionsPerHostSoftLimitAG7NIOCore10TimeAmountV_Sitcfc":{"name":"init(idleTimeout:concurrentHTTP1ConnectionsPerHostSoftLimit:)","abstract":"

Undocumented

","parent_name":"ConnectionPool"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08RedirectC0V8disallowAGvpZ":{"name":"disallow","abstract":"

Redirects are not followed.

","parent_name":"RedirectConfiguration"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08RedirectC0V6follow3max11allowCyclesAGSi_SbtFZ":{"name":"follow(max:allowCycles:)","abstract":"

Redirects are followed with a specified limit.

","parent_name":"RedirectConfiguration"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV7connect7NIOCore10TimeAmountVSgvp":{"name":"connect","abstract":"

Specifies connect timeout. If no connect timeout is given, a default 30 seconds timeout will applied.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV4read7NIOCore10TimeAmountVSgvp":{"name":"read","abstract":"

Specifies read timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV7connect4readAG7NIOCore10TimeAmountVSg_AMtcfc":{"name":"init(connect:read:)","abstract":"

Create timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV4hostSSvp":{"name":"host","abstract":"

Specifies Proxy server host.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV4portSivp":{"name":"port","abstract":"

Specifies Proxy server port.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV13authorizationAC13AuthorizationVSgvp":{"name":"authorization","abstract":"

Specifies Proxy server authorization.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV6server4host4portAGSS_SitFZ":{"name":"server(host:port:)","abstract":"

Create a HTTP proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV6server4host4port13authorizationAGSS_SiAC13AuthorizationVSgtFZ":{"name":"server(host:port:authorization:)","abstract":"

Create a HTTP proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV11socksServer4host4portAGSS_SitFZ":{"name":"socksServer(host:port:)","abstract":"

Create a SOCKSv5 proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC06NIOSSL16TLSConfigurationVSgvp":{"name":"tlsConfiguration","abstract":"

TLS configuration, defaults to TLSConfiguration.makeClientConfiguration().

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08redirectC0AE08RedirectC0Vvp":{"name":"redirectConfiguration","abstract":"

Enables following 3xx redirects automatically, defaults to RedirectConfiguration().

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7timeoutAE7TimeoutVvp":{"name":"timeout","abstract":"

Default client timeout, defaults to no read timeout and 10 seconds connect timeout.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14connectionPoolAE010ConnectionE0Vvp":{"name":"connectionPool","abstract":"

Connection pool configuration.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5proxyAE5ProxyVSgvp":{"name":"proxy","abstract":"

Upstream proxy, defaults to no proxy.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV13decompressionAC13DecompressionOvp":{"name":"decompression","abstract":"

Enables automatic body decompression. Supported algorithms are gzip and deflate.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV24ignoreUncleanSSLShutdownSbvp":{"name":"ignoreUncleanSSLShutdown","abstract":"

Ignore TLS unclean shutdown error, defaults to false.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV11httpVersionAE11HTTPVersionVvp":{"name":"httpVersion","abstract":"

is set to .automatic by default which will use HTTP/2 if run over https and the server supports it, otherwise HTTP/1

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC008redirectC07timeout14connectionPool5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL16TLSConfigurationVSg_AE08RedirectC0VSgAE7TimeoutVAE010ConnectionH0VAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(tlsConfiguration:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC008redirectC07timeout5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL16TLSConfigurationVSg_AE08RedirectC0VSgAE7TimeoutVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(tlsConfiguration:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout38maximumAllowedIdleTimeInConnectionPool5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutV7NIOCore0K6AmountVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:maximumAllowedIdleTimeInConnectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout14connectionPool5proxy24ignoreUncleanSSLShutdown13decompression24backgroundActivityLoggerAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutV7NIOCore10TimeAmountVAE5ProxyVSgSbAC13DecompressionO7Logging0Q0VSgtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:backgroundActivityLogger:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/Proxy.html":{"name":"Proxy","abstract":"

Proxy server configuration","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/Timeout.html":{"name":"Timeout","abstract":"

Timeout configuration.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html":{"name":"RedirectConfiguration","abstract":"

Specifies redirect processing settings.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/ConnectionPool.html":{"name":"ConnectionPool","abstract":"

Connection pool configuration.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/HTTPVersion.html":{"name":"HTTPVersion","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C14eventLoopGroup7NIOCore05EventdE0_pvp":{"name":"eventLoopGroup","abstract":"

Undocumented

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C22eventLoopGroupProvider13configurationA2C05EventdeF0O_AC13ConfigurationVtcfc":{"name":"init(eventLoopGroupProvider:configuration:)","abstract":"

Create an HTTPClient with specified EventLoopGroup provider and configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C22eventLoopGroupProvider13configuration24backgroundActivityLoggerA2C05EventdeF0O_AC13ConfigurationV7Logging0J0Vtcfc":{"name":"init(eventLoopGroupProvider:configuration:backgroundActivityLogger:)","abstract":"

Create an HTTPClient with specified EventLoopGroup provider and configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C12syncShutdownyyKF":{"name":"syncShutdown()","abstract":"

Shuts down the client and EventLoopGroup if it was created by the client.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C8shutdown5queue_y8Dispatch0E5QueueC_ys5Error_pSgctF":{"name":"shutdown(queue:_:)","abstract":"

Shuts down the client and event loop gracefully. This function is clearly an outlier in that it uses a completion","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3get3url8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AG11NIODeadlineVSgtF":{"name":"get(url:deadline:)","abstract":"

Execute GET request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3get3url8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"get(url:deadline:logger:)","abstract":"

Execute GET request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C4post3url4body8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"post(url:body:deadline:)","abstract":"

Execute POST request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C4post3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"post(url:body:deadline:logger:)","abstract":"

Execute POST request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C5patch3url4body8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"patch(url:body:deadline:)","abstract":"

Execute PATCH request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C5patch3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"patch(url:body:deadline:logger:)","abstract":"

Execute PATCH request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3put3url4body8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"put(url:body:deadline:)","abstract":"

Execute PUT request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3put3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"put(url:body:deadline:logger:)","abstract":"

Execute PUT request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C6delete3url8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AG11NIODeadlineVSgtF":{"name":"delete(url:deadline:)","abstract":"

Execute DELETE request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C6delete3url8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"delete(url:deadline:logger:)","abstract":"

Execute DELETE request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_SSAC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:url:body:deadline:logger:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_10socketPath03urlE04body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_S2SAC4BodyVSgAJ11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:socketPath:urlPath:body:deadline:logger:)","abstract":"

Execute arbitrary HTTP+UNIX request to a unix domain socket path, using the specified URL as the request to send to the server.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_16secureSocketPath03urlF04body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_S2SAC4BodyVSgAJ11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:secureSocketPath:urlPath:body:deadline:logger:)","abstract":"

Execute arbitrary HTTPS+UNIX request to a unix domain socket path over TLS, using the specified URL as the request to send to the server.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGAC7RequestV_AG11NIODeadlineVSgtF":{"name":"execute(request:deadline:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGAC7RequestV_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"execute(request:deadline:logger:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request9eventLoop8deadline7NIOCore05EventF6FutureCyAC8ResponseVGAC7RequestV_AC0iF10PreferenceVAH11NIODeadlineVSgtF":{"name":"execute(request:eventLoop:deadline:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request9eventLoop8deadline6logger7NIOCore05EventF6FutureCyAC8ResponseVGAC7RequestV_AC0jF10PreferenceVAI11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(request:eventLoop:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate8deadlineAC4TaskCy_8ResponseQzGAC7RequestV_x7NIOCore11NIODeadlineVSgtAA0bH8DelegateRzlF":{"name":"execute(request:delegate:deadline:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate8deadline6loggerAC4TaskCy_8ResponseQzGAC7RequestV_x7NIOCore11NIODeadlineVSg7Logging6LoggerVtAA0bI8DelegateRzlF":{"name":"execute(request:delegate:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate9eventLoop8deadlineAC4TaskCy_8ResponseQzGAC7RequestV_xAC05EventG10PreferenceV7NIOCore11NIODeadlineVSgtAA0bJ8DelegateRzlF":{"name":"execute(request:delegate:eventLoop:deadline:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate9eventLoop8deadline6loggerAC4TaskCy_8ResponseQzGAC7RequestV_xAC05EventG10PreferenceV7NIOCore11NIODeadlineVSg7Logging6LoggerVSgtAA0bK8DelegateRzlF":{"name":"execute(request:delegate:eventLoop:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Configuration.html":{"name":"Configuration","abstract":"

HTTPClient configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient/EventLoopGroupProvider.html":{"name":"EventLoopGroupProvider","abstract":"

Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

","parent_name":"HTTPClient"},"Classes/HTTPClient/EventLoopPreference.html":{"name":"EventLoopPreference","abstract":"

Specifies how the library will treat event loop passed by the user.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Decompression.html":{"name":"Decompression","abstract":"

Specifies decompression settings.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Cookie.html":{"name":"Cookie","abstract":"

A representation of an HTTP cookie.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Body.html":{"name":"Body","abstract":"

Represent request body.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Response.html":{"name":"Response","abstract":"

Represent HTTP response.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Request.html":{"name":"Request","abstract":"

Represent HTTP request.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Authorization.html":{"name":"Authorization","abstract":"

HTTP authentication

","parent_name":"HTTPClient"},"Classes/HTTPClient/Task.html":{"name":"Task","abstract":"

Response execution context. Will be created by the library and could be used for obtaining","parent_name":"HTTPClient"},"Classes/HTTPClient/NWPOSIXError.html":{"name":"NWPOSIXError","parent_name":"HTTPClient"},"Classes/HTTPClient/NWTLSError.html":{"name":"NWTLSError","parent_name":"HTTPClient"},"Classes/FileDownloadDelegate/Progress.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8ProgressV10totalBytesSiSgvp":{"name":"totalBytes","abstract":"

Undocumented

","parent_name":"Progress"},"Classes/FileDownloadDelegate/Progress.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8ProgressV13receivedBytesSivp":{"name":"receivedBytes","abstract":"

Undocumented

","parent_name":"Progress"},"Classes/FileDownloadDelegate/Progress.html":{"name":"Progress","abstract":"

The response type for this delegate: the total count of bytes as reported by the response","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8Responsea":{"name":"Response","abstract":"

Undocumented

","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient20FileDownloadDelegateC4path4pool10reportHead0H8ProgressACSS_8NIOPosix13NIOThreadPoolCy8NIOHTTP1012HTTPResponseI0VcSgyAC0J0VcSgtKcfc":{"name":"init(path:pool:reportHead:reportProgress:)","abstract":"

Initializes a new file download delegate.

","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html":{"name":"FileDownloadDelegate","abstract":"

Handles a streaming download to a given file path, allowing headers and progress to be reported.

"},"Classes/HTTPClient.html":{"name":"HTTPClient","abstract":"

HTTPClient class provides API for request execution.

"},"Classes/ResponseAccumulator.html":{"name":"ResponseAccumulator","abstract":"

Undocumented

"},"Classes/HTTPClientCopyingDelegate.html":{"name":"HTTPClientCopyingDelegate","abstract":"

Undocumented

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Extensions.html":{"name":"Extensions","abstract":"

The following extensions are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"},"Structs.html":{"name":"Structures","abstract":"

The following structures are available globally.

"}} \ No newline at end of file diff --git a/docs/1.9.1/AsyncHTTPClient/undocumented.json b/docs/1.9.1/AsyncHTTPClient/undocumented.json new file mode 100644 index 000000000..d1ea1259d --- /dev/null +++ b/docs/1.9.1/AsyncHTTPClient/undocumented.json @@ -0,0 +1,201 @@ +{ + "warnings": [ + { + "file": "/code/Sources/AsyncHTTPClient/ConnectionPool/HTTPConnectionPool+Manager.swift", + "line": 128, + "symbol": "HTTPConnectionPool.Manager", + "symbol_kind": "source.lang.swift.decl.extension", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/ConnectionPool/HTTPRequestStateMachine.swift", + "line": 779, + "symbol": "HTTPRequestStateMachine.ResponseState", + "symbol_kind": "source.lang.swift.decl.extension", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP1StateMachine.swift", + "line": 617, + "symbol": "HTTPConnectionPool.HTTP1StateMachine", + "symbol_kind": "source.lang.swift.decl.extension", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+StateMachine.swift", + "line": 303, + "symbol": "HTTPConnectionPool.StateMachine", + "symbol_kind": "source.lang.swift.decl.extension", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 24, + "symbol": "FileDownloadDelegate.Progress.totalBytes", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 25, + "symbol": "FileDownloadDelegate.Progress.receivedBytes", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 30, + "symbol": "FileDownloadDelegate.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 67, + "symbol": "HTTPClient.eventLoopGroup", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 658, + "symbol": "HTTPClient.Configuration.init(tlsConfiguration:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 676, + "symbol": "HTTPClient.Configuration.init(tlsConfiguration:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 693, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:maximumAllowedIdleTimeInConnectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 711, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:backgroundActivityLogger:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 730, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 878, + "symbol": "HTTPClient.Configuration.ConnectionPool.init(idleTimeout:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 882, + "symbol": "HTTPClient.Configuration.ConnectionPool.init(idleTimeout:concurrentHTTP1ConnectionsPerHostSoftLimit:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 888, + "symbol": "HTTPClient.Configuration.HTTPVersion", + "symbol_kind": "source.lang.swift.decl.struct", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 1029, + "symbol": "HTTPClientError.httpEndReceivedAfterHeadWith1xx", + "symbol_kind": "source.lang.swift.decl.var.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 288, + "symbol": "HTTPClient.Authorization.basic(username:password:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 292, + "symbol": "HTTPClient.Authorization.basic(credentials:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 296, + "symbol": "HTTPClient.Authorization.bearer(tokens:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 300, + "symbol": "HTTPClient.Authorization.headerValue", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 311, + "symbol": "ResponseAccumulator", + "symbol_kind": "source.lang.swift.decl.class", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 312, + "symbol": "ResponseAccumulator.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 325, + "symbol": "ResponseAccumulator.init(request:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 418, + "symbol": "HTTPClientResponseDelegate.Response", + "symbol_kind": "source.lang.swift.decl.associatedtype", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 17, + "symbol": "HTTPClientCopyingDelegate", + "symbol_kind": "source.lang.swift.decl.class", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 18, + "symbol": "HTTPClientCopyingDelegate.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 22, + "symbol": "HTTPClientCopyingDelegate.init(chunkHandler:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + } + ], + "source_directory": "/code" +} \ No newline at end of file diff --git a/docs/current/AsyncHTTPClient/Classes.html b/docs/current/AsyncHTTPClient/Classes.html new file mode 100644 index 000000000..b585a0226 --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes.html @@ -0,0 +1,314 @@ + + + + Classes Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Classes

+

The following classes are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + FileDownloadDelegate + +
    +
    +
    +
    +
    +
    +

    Handles a streaming download to a given file path, allowing headers and progress to be reported.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class FileDownloadDelegate : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + HTTPClient + +
    +
    +
    +
    +
    +
    +

    HTTPClient class provides API for request execution.

    + +

    Example:

    +
        let client = HTTPClient(eventLoopGroupProvider: .createNew)
    +    client.get(url: "https://swift.org/", deadline: .now() + .seconds(1)).whenComplete { result in
    +        switch result {
    +        case .failure(let error):
    +            // process error
    +        case .success(let response):
    +            if let response.status == .ok {
    +                // handle response
    +            } else {
    +                // handle remote error
    +            }
    +        }
    +    }
    +
    + +

    It is important to close the client instance, for example in a defer statement, after use to cleanly shutdown the underlying NIO EventLoopGroup:

    +
        try client.syncShutdown()
    +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class HTTPClient
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + ResponseAccumulator + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class ResponseAccumulator : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Undocumented

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class HTTPClientCopyingDelegate : HTTPClientResponseDelegate
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Classes/FileDownloadDelegate.html b/docs/current/AsyncHTTPClient/Classes/FileDownloadDelegate.html new file mode 100644 index 000000000..4f6a0c533 --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes/FileDownloadDelegate.html @@ -0,0 +1,457 @@ + + + + FileDownloadDelegate Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

FileDownloadDelegate

+
+
+ +
public final class FileDownloadDelegate : HTTPClientResponseDelegate
+ +
+
+

Handles a streaming download to a given file path, allowing headers and progress to be reported.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + Progress + +
    +
    +
    +
    +
    +
    +

    The response type for this delegate: the total count of bytes as reported by the response +“Content-Length” header (if available) and the count of bytes downloaded.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Progress
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public typealias Response = Progress
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Initializes a new file download delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(
    +    path: String,
    +    pool: NIOThreadPool = NIOThreadPool(numberOfThreads: 1),
    +    reportHead: ((HTTPResponseHead) -> Void)? = nil,
    +    reportProgress: ((Progress) -> Void)? = nil
    +) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + path + + +
    +

    Path to a file you’d like to write the download to.

    +
    +
    + + pool + + +
    +

    A thread pool to use for asynchronous file I/O.

    +
    +
    + + reportHead + + +
    +

    A closure called when the response head is available.

    +
    +
    + + reportProgress + + +
    +

    A closure called when a body chunk has been downloaded, with +the total byte count and download byte count passed to it as arguments. The callbacks +will be invoked in the same threading context that the delegate itself is invoked, +as controlled by EventLoopPreference.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveHead(
    +    task: HTTPClient.Task<Response>,
    +    _ head: HTTPResponseHead
    +) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveBodyPart(
    +    task: HTTPClient.Task<Response>,
    +    _ buffer: ByteBuffer
    +) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didReceiveError(task: HTTPClient.Task<Progress>, _ error: Error)
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func didFinishRequest(task: HTTPClient.Task<Response>) throws -> Response
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html b/docs/current/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html new file mode 100644 index 000000000..4e230ab98 --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes/FileDownloadDelegate/Progress.html @@ -0,0 +1,241 @@ + + + + Progress Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Progress

+
+
+ +
public struct Progress
+ +
+
+

The response type for this delegate: the total count of bytes as reported by the response +“Content-Length” header (if available) and the count of bytes downloaded.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + totalBytes + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var totalBytes: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + receivedBytes + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var receivedBytes: Int
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Classes/HTTPClient.html b/docs/current/AsyncHTTPClient/Classes/HTTPClient.html new file mode 100644 index 000000000..536905524 --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes/HTTPClient.html @@ -0,0 +1,2472 @@ + + + + HTTPClient Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClient

+
+
+ +
public class HTTPClient
+ +
+
+

HTTPClient class provides API for request execution.

+ +

Example:

+
    let client = HTTPClient(eventLoopGroupProvider: .createNew)
+    client.get(url: "https://swift.org/", deadline: .now() + .seconds(1)).whenComplete { result in
+        switch result {
+        case .failure(let error):
+            // process error
+        case .success(let response):
+            if let response.status == .ok {
+                // handle response
+            } else {
+                // handle remote error
+            }
+        }
+    }
+
+ +

It is important to close the client instance, for example in a defer statement, after use to cleanly shutdown the underlying NIO EventLoopGroup:

+
    try client.syncShutdown()
+
+ + +
+
+ +
+
+
+
    +
  • +
    + + + + eventLoopGroup + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let eventLoopGroup: EventLoopGroup
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTPClient with specified EventLoopGroup provider and configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public convenience init(eventLoopGroupProvider: EventLoopGroupProvider,
    +                        configuration: Configuration = Configuration())
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + eventLoopGroupProvider + + +
    +

    Specify how EventLoopGroup will be created.

    +
    +
    + + configuration + + +
    +

    Client configuration.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTPClient with specified EventLoopGroup provider and configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public required init(eventLoopGroupProvider: EventLoopGroupProvider,
    +                     configuration: Configuration = Configuration(),
    +                     backgroundActivityLogger: Logger)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + eventLoopGroupProvider + + +
    +

    Specify how EventLoopGroup will be created.

    +
    +
    + + configuration + + +
    +

    Client configuration.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + syncShutdown() + +
    +
    +
    +
    +
    +
    +

    Shuts down the client and EventLoopGroup if it was created by the client.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func syncShutdown() throws
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + shutdown(queue:_:) + +
    +
    +
    +
    +
    +
    +

    Shuts down the client and event loop gracefully. This function is clearly an outlier in that it uses a completion +callback instead of an EventLoopFuture. The reason for that is that NIO’s EventLoopFutures will call back on an event loop. +The virtue of this function is to shut the event loop down. To work around that we call back on a DispatchQueue +instead.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func shutdown(queue: DispatchQueue = .global(), _ callback: @escaping (Error?) -> Void)
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + get(url:deadline:) + +
    +
    +
    +
    +
    +
    +

    Execute GET request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func get(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute GET request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func get(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute POST request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute POST request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func post(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PATCH request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PATCH request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func patch(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PUT request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute PUT request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func put(url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + delete(url:deadline:) + +
    +
    +
    +
    +
    +
    +

    Execute DELETE request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func delete(url: String, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    The time when the request must have been completed by.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute DELETE request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func delete(url: String, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + deadline + + +
    +

    The time when the request must have been completed by.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, url: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + url + + +
    +

    Request url.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP+UNIX request to a unix domain socket path, using the specified URL as the request to send to the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, socketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + urlPath + + +
    +

    The URL path and query that will be sent to the server.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTPS+UNIX request to a unix domain socket path over TLS, using the specified URL as the request to send to the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(_ method: HTTPMethod = .GET, secureSocketPath: String, urlPath: String, body: Body? = nil, deadline: NIODeadline? = nil, logger: Logger? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + method + + +
    +

    Request method.

    +
    +
    + + secureSocketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + urlPath + + +
    +

    The URL path and query that will be sent to the server.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, deadline: NIODeadline? = nil, logger: Logger) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request using specified URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request, eventLoop: EventLoopPreference, deadline: NIODeadline? = nil) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute(request: Request,
    +                    eventLoop eventLoopPreference: EventLoopPreference,
    +                    deadline: NIODeadline? = nil,
    +                    logger: Logger?) -> EventLoopFuture<Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          deadline: NIODeadline? = nil) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          deadline: NIODeadline? = nil,
    +                                                          logger: Logger) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          eventLoop eventLoopPreference: EventLoopPreference,
    +                                                          deadline: NIODeadline? = nil) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    + + logger + + +
    +

    The logger to use for this request.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Execute arbitrary HTTP request and handle response processing using provided delegate.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func execute<Delegate: HTTPClientResponseDelegate>(request: Request,
    +                                                          delegate: Delegate,
    +                                                          eventLoop eventLoopPreference: EventLoopPreference,
    +                                                          deadline: NIODeadline? = nil,
    +                                                          logger originalLogger: Logger?) -> Task<Delegate.Response>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + request + + +
    +

    HTTP request to execute.

    +
    +
    + + delegate + + +
    +

    Delegate to process response parts.

    +
    +
    + + eventLoop + + +
    +

    NIO Event Loop preference.

    +
    +
    + + deadline + + +
    +

    Point in time by which the request must complete.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + Configuration + +
    +
    +
    +
    +
    +
    +

    HTTPClient configuration.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Configuration
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum EventLoopGroupProvider
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + EventLoopPreference + +
    +
    +
    +
    +
    +
    +

    Specifies how the library will treat event loop passed by the user.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct EventLoopPreference
    +
    extension HTTPClient.EventLoopPreference: CustomStringConvertible
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Decompression + +
    +
    +
    +
    +
    +
    +

    Specifies decompression settings.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum Decompression
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Cookie + +
    +
    +
    +
    +
    +
    +

    A representation of an HTTP cookie.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Cookie
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Represent HTTP response.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Response
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Body + +
    +
    +
    +
    +
    +
    +

    Represent request body.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Body
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Request + +
    +
    +
    +
    +
    +
    +

    Represent HTTP request.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Request
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Authorization + +
    +
    +
    +
    +
    +
    +

    HTTP authentication

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct Authorization : Hashable
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + Task + +
    +
    +
    +
    +
    +
    +

    Response execution context. Will be created by the library and could be used for obtaining +EventLoopFuture<Response> of the execution or cancellation of the execution.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public final class Task<Response>
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + NWPOSIXError + +
    +
    +
    +
    +
    +
    + + See more +
    +
    +
    +
  • +
  • +
    + + + + NWTLSError + +
    +
    +
    +
    +
    +
    + + See more +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Classes/HTTPClient/Authorization.html b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Authorization.html new file mode 100644 index 000000000..f7e63b323 --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Authorization.html @@ -0,0 +1,300 @@ + + + + Authorization Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Authorization

+
+
+ +
public struct Authorization : Hashable
+ +
+
+

HTTP authentication

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Classes/HTTPClient/Body.html b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Body.html new file mode 100644 index 000000000..5e32b098c --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Body.html @@ -0,0 +1,481 @@ + + + + Body Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Body

+
+
+ +
public struct Body
+ +
+
+

Represent request body.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + StreamWriter + +
    +
    +
    +
    +
    +
    +

    Chunk provider.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct StreamWriter
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + length + +
    +
    +
    +
    +
    +
    +

    Body size. Request validation will be failed with HTTPClientErrors.contentLengthMissing if nil, +unless Trasfer-Encoding: chunked header is set.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var length: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + stream + +
    +
    +
    +
    +
    +
    +

    Body chunk provider.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var stream: (StreamWriter) -> EventLoopFuture<Void>
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + byteBuffer(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using ByteBuffer.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func byteBuffer(_ buffer: ByteBuffer) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + buffer + + +
    +

    Body ByteBuffer representation.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + stream(length:_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using StreamWriter.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func stream(length: Int? = nil, _ stream: @escaping (StreamWriter) -> EventLoopFuture<Void>) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + length + + +
    +

    Body size. Request validation will be failed with HTTPClientErrors.contentLengthMissing if nil, + unless Transfer-Encoding: chunked header is set.

    +
    +
    + + stream + + +
    +

    Body chunk provider.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + data(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using Data.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func data(_ data: Data) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    Body Data representation.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + string(_:) + +
    +
    +
    +
    +
    +
    +

    Create and stream body using String.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func string(_ string: String) -> Body
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + string + + +
    +

    Body String representation.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html new file mode 100644 index 000000000..13f114ecd --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Body/StreamWriter.html @@ -0,0 +1,278 @@ + + + + StreamWriter Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

StreamWriter

+
+
+ +
public struct StreamWriter
+ +
+
+

Chunk provider.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + init(closure:) + +
    +
    +
    +
    +
    +
    +

    Create new StreamWriter

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(closure: @escaping (IOData) -> EventLoopFuture<Void>)
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + closure + + +
    +

    function that will be called to write actual bytes to the channel.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + write(_:) + +
    +
    +
    +
    +
    +
    +

    Write data to server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func write(_ data: IOData) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + data + + +
    +

    IOData to write.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Classes/HTTPClient/Configuration.html b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Configuration.html new file mode 100644 index 000000000..ae8745fcd --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Configuration.html @@ -0,0 +1,714 @@ + + + + Configuration Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Configuration

+
+
+ +
public struct Configuration
+ +
+
+

HTTPClient configuration.

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html new file mode 100644 index 000000000..2248ca438 --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Configuration/ConnectionPool.html @@ -0,0 +1,240 @@ + + + + ConnectionPool Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

ConnectionPool

+
+
+ +
public struct ConnectionPool : Hashable
+ +
+
+

Connection pool configuration.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + idleTimeout + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var idleTimeout: TimeAmount
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + init(idleTimeout:) + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(idleTimeout: TimeAmount = .seconds(60))
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html new file mode 100644 index 000000000..03114db60 --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Configuration/Proxy.html @@ -0,0 +1,478 @@ + + + + Proxy Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Proxy

+
+
+ +
public struct Proxy
+ +
+
+

Proxy server configuration +Specifies the remote address of an HTTP proxy.

+ +

Adding an Proxy to your client’s HTTPClient.Configuration +will cause requests to be passed through the specified proxy using the +HTTP CONNECT method.

+ +

If a TLSConfiguration is used in conjunction with HTTPClient.Configuration.Proxy, +TLS will be established after successful proxy, between your client +and the destination server.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server host.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + port + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server port.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var port: Int
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + authorization + +
    +
    +
    +
    +
    +
    +

    Specifies Proxy server authorization.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var authorization: HTTPClient.Authorization? { get set }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + server(host:port:) + +
    +
    +
    +
    +
    +
    +

    Create a HTTP proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func server(host: String, port: Int) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + host + + +
    +

    proxy server host.

    +
    +
    + + port + + +
    +

    proxy server port.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a HTTP proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func server(host: String, port: Int, authorization: HTTPClient.Authorization? = nil) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + +
    + + host + + +
    +

    proxy server host.

    +
    +
    + + port + + +
    +

    proxy server port.

    +
    +
    + + authorization + + +
    +

    proxy server authorization.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a SOCKSv5 proxy.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func socksServer(host: String, port: Int = 1080) -> Proxy
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + host + + +
    +

    The SOCKSv5 proxy address.

    +
    +
    + + port + + +
    +

    The SOCKSv5 proxy port, defaults to 1080.

    +
    +
    +
    +
    +

    Return Value

    +

    A new instance of Proxy configured to connect to a SOCKSv5 server.

    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html new file mode 100644 index 000000000..4f3fb2f1f --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Configuration/RedirectConfiguration.html @@ -0,0 +1,276 @@ + + + + RedirectConfiguration Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

RedirectConfiguration

+
+
+ +
public struct RedirectConfiguration
+ +
+
+

Specifies redirect processing settings.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + disallow + +
    +
    +
    +
    +
    +
    +

    Redirects are not followed.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let disallow: HTTPClient.Configuration.RedirectConfiguration
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Redirects are followed with a specified limit.

    +
    +

    Warning

    +

    Cycle detection will keep all visited URLs in memory which means a malicious server could use this as a denial-of-service vector.

    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func follow(max: Int, allowCycles: Bool) -> RedirectConfiguration
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + max + + +
    +

    The maximum number of allowed redirects.

    +
    +
    + + allowCycles + + +
    +

    Whether cycles are allowed.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html new file mode 100644 index 000000000..660338ee3 --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Configuration/Timeout.html @@ -0,0 +1,301 @@ + + + + Timeout Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Timeout

+
+
+ +
public struct Timeout
+ +
+
+

Timeout configuration.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + connect + +
    +
    +
    +
    +
    +
    +

    Specifies connect timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var connect: TimeAmount?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + read + +
    +
    +
    +
    +
    +
    +

    Specifies read timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var read: TimeAmount?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + init(connect:read:) + +
    +
    +
    +
    +
    +
    +

    Create timeout.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(connect: TimeAmount? = nil, read: TimeAmount? = nil)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + connect + + +
    +

    connect timeout.

    +
    +
    + + read + + +
    +

    read timeout.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Classes/HTTPClient/Cookie.html b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Cookie.html new file mode 100644 index 000000000..04c170de9 --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Cookie.html @@ -0,0 +1,618 @@ + + + + Cookie Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Cookie

+
+
+ +
public struct Cookie
+ +
+
+

A representation of an HTTP cookie.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + name + +
    +
    +
    +
    +
    +
    +

    The name of the cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var name: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + value + +
    +
    +
    +
    +
    +
    +

    The cookie’s string value.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var value: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + path + +
    +
    +
    +
    +
    +
    +

    The cookie’s path.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var path: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + domain + +
    +
    +
    +
    +
    +
    +

    The domain of the cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var domain: String?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + expires + +
    +
    +
    +
    +
    +
    +

    The cookie’s expiration date.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var expires: Date?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + maxAge + +
    +
    +
    +
    +
    +
    +

    The cookie’s age in seconds.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var maxAge: Int?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + httpOnly + +
    +
    +
    +
    +
    +
    +

    Whether the cookie should only be sent to HTTP servers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var httpOnly: Bool
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + secure + +
    +
    +
    +
    +
    +
    +

    Whether the cookie should only be sent over secure channels.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var secure: Bool
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create a Cookie by parsing a Set-Cookie header.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(header: String, defaultDomain: String)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + header + + +
    +

    String representation of the Set-Cookie response header.

    +
    +
    + + defaultDomain + + +
    +

    Default domain to use if cookie was sent without one.

    +
    +
    +
    +
    +

    Return Value

    +

    nil if the header is invalid.

    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP cookie.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(name: String, value: String, path: String = "/", domain: String? = nil, expires: Date? = nil, maxAge: Int? = nil, httpOnly: Bool = false, secure: Bool = false)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + name + + +
    +

    The name of the cookie.

    +
    +
    + + value + + +
    +

    The cookie’s string value.

    +
    +
    + + path + + +
    +

    The cookie’s path.

    +
    +
    + + domain + + +
    +

    The domain of the cookie, defaults to nil.

    +
    +
    + + expires + + +
    +

    The cookie’s expiration date, defaults to nil.

    +
    +
    + + maxAge + + +
    +

    The cookie’s age in seconds, defaults to nil.

    +
    +
    + + httpOnly + + +
    +

    Whether this cookie should be used by HTTP servers only, defaults to false.

    +
    +
    + + secure + + +
    +

    Whether this cookie should only be sent using secure channels, defaults to false.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Classes/HTTPClient/Decompression.html b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Decompression.html new file mode 100644 index 000000000..c632917f8 --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Decompression.html @@ -0,0 +1,240 @@ + + + + Decompression Enumeration Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Decompression

+
+
+ +
public enum Decompression
+ +
+
+

Specifies decompression settings.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + disabled + +
    +
    +
    +
    +
    +
    +

    Decompression is disabled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case disabled
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + enabled(limit:) + +
    +
    +
    +
    +
    +
    +

    Decompression is enabled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case enabled(limit: NIOHTTPDecompression.DecompressionLimit)
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html b/docs/current/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html new file mode 100644 index 000000000..5c4056ec5 --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes/HTTPClient/EventLoopGroupProvider.html @@ -0,0 +1,240 @@ + + + + EventLoopGroupProvider Enumeration Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

EventLoopGroupProvider

+
+
+ +
public enum EventLoopGroupProvider
+ +
+
+

Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + shared(_:) + +
    +
    +
    +
    +
    +
    +

    EventLoopGroup will be provided by the user. Owner of this group is responsible for its lifecycle.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case shared(EventLoopGroup)
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + createNew + +
    +
    +
    +
    +
    +
    +

    EventLoopGroup will be created by the client. When syncShutdown is called, created EventLoopGroup will be shut down as well.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    case createNew
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html b/docs/current/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html new file mode 100644 index 000000000..fe290b953 --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes/HTTPClient/EventLoopPreference.html @@ -0,0 +1,307 @@ + + + + EventLoopPreference Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

EventLoopPreference

+
+
+ +
public struct EventLoopPreference
+
extension HTTPClient.EventLoopPreference: CustomStringConvertible
+ +
+
+

Specifies how the library will treat event loop passed by the user.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + indifferent + +
    +
    +
    +
    +
    +
    +

    Event Loop will be selected by the library.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let indifferent: HTTPClient.EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + delegate(on:) + +
    +
    +
    +
    +
    +
    +

    The delegate will be run on the specified EventLoop (and the Channel if possible).

    + +

    This will call the configured delegate on eventLoop and will try to use a Channel on the same +EventLoop but will not establish a new network connection just to satisfy the EventLoop preference if +another existing connection on a different EventLoop is readily available from a connection pool.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func delegate(on eventLoop: EventLoop) -> EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The delegate and the Channel will be run on the specified EventLoop.

    + +

    Use this for use-cases where you prefer a new connection to be established over re-using an existing +connection that might be on a different EventLoop.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func delegateAndChannel(on eventLoop: EventLoop) -> EventLoopPreference
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var description: String { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Classes/HTTPClient/NWPOSIXError.html b/docs/current/AsyncHTTPClient/Classes/HTTPClient/NWPOSIXError.html new file mode 100644 index 000000000..6a094a48d --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes/HTTPClient/NWPOSIXError.html @@ -0,0 +1,225 @@ + + + + NWPOSIXError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

NWPOSIXError

+ +
+
+ +
+
+
+
    +
  • +
    + + + + errorCode + +
    +
    +
    +
    +
    +
    +

    POSIX error code (enum)

    + +
    +
    +
    +
  • +
  • +
    + + + + init(_:reason:) + +
    +
    +
    +
    +
    +
    +

    Initialise a NWPOSIXError

    + +
    +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Classes/HTTPClient/NWTLSError.html b/docs/current/AsyncHTTPClient/Classes/HTTPClient/NWTLSError.html new file mode 100644 index 000000000..2d1660d9d --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes/HTTPClient/NWTLSError.html @@ -0,0 +1,225 @@ + + + + NWTLSError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

NWTLSError

+ +
+
+ +
+
+
+
    +
  • +
    + + + + status + +
    +
    +
    +
    +
    +
    +

    TLS error status. List of TLS errors can be found in

    + +
    +
    +
    +
  • +
  • +
    + + + + init(_:reason:) + +
    +
    +
    +
    +
    +
    +

    initialise a NWTLSError

    + +
    +
    +
    +
  • +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Classes/HTTPClient/Request.html b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Request.html new file mode 100644 index 000000000..e610c4526 --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Request.html @@ -0,0 +1,878 @@ + + + + Request Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Request

+
+
+ +
public struct Request
+ +
+
+

Represent HTTP request.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + method + +
    +
    +
    +
    +
    +
    +

    Request HTTP method, defaults to GET.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let method: HTTPMethod
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + url + +
    +
    +
    +
    +
    +
    +

    Remote URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let url: URL
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + scheme + +
    +
    +
    +
    +
    +
    +

    Remote HTTP scheme, resolved from URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let scheme: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Remote host, resolved from URL.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + headers + +
    +
    +
    +
    +
    +
    +

    Request custom HTTP Headers, defaults to no headers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var headers: HTTPHeaders
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + body + +
    +
    +
    +
    +
    +
    +

    Request body, defaults to no body.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var body: Body?
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + tlsConfiguration + +
    +
    +
    +
    +
    +
    +

    Request-specific TLS configuration, defaults to no request-specific TLS configuration.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var tlsConfiguration: TLSConfiguration?
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP request.

    +
    +

    Throws

    +
      +
    • invalidURL if URL cannot be parsed.
    • +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: String, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + version + + +
    +

    HTTP version.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP request.

    +
    +

    Throws

    +
      +
    • invalidURL if URL cannot be parsed.
    • +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: String, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil, tlsConfiguration: TLSConfiguration?) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + version + + +
    +

    HTTP version.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + tlsConfiguration + + +
    +

    Request TLS configuration

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTP Request.

    +
    +

    Throws

    +
      +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    • missingSocketPath if URL does not contains a socketPath as an encoded host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create an HTTP Request.

    +
    +

    Throws

    +
      +
    • emptyScheme if URL does not contain HTTP scheme.
    • +
    • unsupportedScheme if URL does contains unsupported HTTP scheme.
    • +
    • emptyHost if URL does not contains a host.
    • +
    • missingSocketPath if URL does not contains a socketPath as an encoded host.
    • +
    + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: Body? = nil, tlsConfiguration: TLSConfiguration?) throws
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + url + + +
    +

    Remote URL.

    +
    +
    + + method + + +
    +

    HTTP method.

    +
    +
    + + headers + + +
    +

    Custom HTTP headers.

    +
    +
    + + body + + +
    +

    Request body.

    +
    +
    + + tlsConfiguration + + +
    +

    Request TLS configuration

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + useTLS + +
    +
    +
    +
    +
    +
    +

    Whether request will be executed using secure socket.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var useTLS: Bool { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + port + +
    +
    +
    +
    +
    +
    +

    Resolved port.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var port: Int { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Classes/HTTPClient/Response.html b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Response.html new file mode 100644 index 000000000..c31e20e93 --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Response.html @@ -0,0 +1,543 @@ + + + + Response Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Response

+
+
+ +
public struct Response
+ +
+
+

Represent HTTP response.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + host + +
    +
    +
    +
    +
    +
    +

    Remote host of the request.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var host: String
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + status + +
    +
    +
    +
    +
    +
    +

    Response HTTP status.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var status: HTTPResponseStatus
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + version + +
    +
    +
    +
    +
    +
    +

    Response HTTP version.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var version: HTTPVersion
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + headers + +
    +
    +
    +
    +
    +
    +

    Reponse HTTP headers.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var headers: HTTPHeaders
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + body + +
    +
    +
    +
    +
    +
    +

    Response body.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var body: ByteBuffer?
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP Response.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    @available(*, deprecated, renamed: "init(host:status:version:headers:body:﹚")
    +public init(host: String, status: HTTPResponseStatus, headers: HTTPHeaders, body: ByteBuffer?)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + +
    + + host + + +
    +

    Remote host of the request.

    +
    +
    + + status + + +
    +

    Response HTTP status.

    +
    +
    + + headers + + +
    +

    Reponse HTTP headers.

    +
    +
    + + body + + +
    +

    Response body.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Create HTTP Response.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init(host: String, status: HTTPResponseStatus, version: HTTPVersion, headers: HTTPHeaders, body: ByteBuffer?)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + + + + + + + + + + + + + +
    + + host + + +
    +

    Remote host of the request.

    +
    +
    + + status + + +
    +

    Response HTTP status.

    +
    +
    + + version + + +
    +

    Response HTTP version.

    +
    +
    + + headers + + +
    +

    Reponse HTTP headers.

    +
    +
    + + body + + +
    +

    Response body.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + cookies + +
    +
    +
    +
    +
    +
    +

    List of HTTP cookies returned by the server.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var cookies: [HTTPClient.Cookie] { get }
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Classes/HTTPClient/Task.html b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Task.html new file mode 100644 index 000000000..47aece8ee --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes/HTTPClient/Task.html @@ -0,0 +1,310 @@ + + + + Task Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Task

+
+
+ +
public final class Task<Response>
+ +
+
+

Response execution context. Will be created by the library and could be used for obtaining +EventLoopFuture<Response> of the execution or cancellation of the execution.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + eventLoop + +
    +
    +
    +
    +
    +
    +

    The EventLoop the delegate will be executed on.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public let eventLoop: EventLoop
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + futureResult + +
    +
    +
    +
    +
    +
    +

    EventLoopFuture for the response returned by this request.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var futureResult: EventLoopFuture<Response> { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + wait() + +
    +
    +
    +
    +
    +
    +

    Waits for execution of this request to complete.

    +
    +

    Throws

    + The error value of the EventLoopFuture if it errors. + +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func wait() throws -> Response
    + +
    +
    +
    +

    Return Value

    +

    The value of the EventLoopFuture when it completes.

    +
    + +
    +
    +
  • +
  • +
    + + + + cancel() + +
    +
    +
    +
    +
    +
    +

    Cancels the request execution.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public func cancel()
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html b/docs/current/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html new file mode 100644 index 000000000..b05decb48 --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes/HTTPClientCopyingDelegate.html @@ -0,0 +1,298 @@ + + + + HTTPClientCopyingDelegate Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientCopyingDelegate

+
+
+ +
public final class HTTPClientCopyingDelegate : HTTPClientResponseDelegate
+ +
+
+

Undocumented

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Classes/ResponseAccumulator.html b/docs/current/AsyncHTTPClient/Classes/ResponseAccumulator.html new file mode 100644 index 000000000..c70285d46 --- /dev/null +++ b/docs/current/AsyncHTTPClient/Classes/ResponseAccumulator.html @@ -0,0 +1,356 @@ + + + + ResponseAccumulator Class Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

ResponseAccumulator

+
+
+ +
public class ResponseAccumulator : HTTPClientResponseDelegate
+ +
+
+

Undocumented

+ + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Extensions.html b/docs/current/AsyncHTTPClient/Extensions.html new file mode 100644 index 000000000..549986320 --- /dev/null +++ b/docs/current/AsyncHTTPClient/Extensions.html @@ -0,0 +1,215 @@ + + + + Extensions Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Extensions

+

The following extensions are available globally.

+ +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Extensions/URL.html b/docs/current/AsyncHTTPClient/Extensions/URL.html new file mode 100644 index 000000000..303052db4 --- /dev/null +++ b/docs/current/AsyncHTTPClient/Extensions/URL.html @@ -0,0 +1,298 @@ + + + + URL Extension Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

URL

+
+
+ +
extension URL
+ +
+
+ +
+
+ +
+
+
+
    +
  • + +
    +
    +
    +
    +
    +

    Initializes a newly created HTTP URL connecting to a unix domain socket path. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “http+unix” scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(httpURLWithSocketPath socketPath: String, uri: String = "/")
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + uri + + +
    +

    The URI path and query that will be sent to the server.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Initializes a newly created HTTPS URL connecting to a unix domain socket path over TLS. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “https+unix” scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public init?(httpsURLWithSocketPath socketPath: String, uri: String = "/")
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + socketPath + + +
    +

    The path to the unix domain socket to connect to.

    +
    +
    + + uri + + +
    +

    The URI path and query that will be sent to the server.

    +
    +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Protocols.html b/docs/current/AsyncHTTPClient/Protocols.html new file mode 100644 index 000000000..549e4efbd --- /dev/null +++ b/docs/current/AsyncHTTPClient/Protocols.html @@ -0,0 +1,233 @@ + + + + Protocols Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Protocols

+

The following protocols are available globally.

+ +
+
+ +
+
+
+
    +
  • + +
    +
    +
    +
    +
    +

    HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed. +You can implement this protocol if you need fine-grained control over an HTTP request/response, for example, if you want to inspect the response +headers before deciding whether to accept a response body, or if you want to stream your request body. Pass an instance of your conforming +class to the HTTPClient.execute() method and this package will call each delegate method appropriately as the request takes place./

    +

    Backpressure

    + +

    A HTTPClientResponseDelegate can be used to exert backpressure on the server response. This is achieved by way of the futures returned from +didReceiveHead and didReceiveBodyPart. The following functions are part of the “backpressure system” in the delegate:

    + +
      +
    • didReceiveHead
    • +
    • didReceiveBodyPart
    • +
    • didFinishRequest
    • +
    • didReceiveError
    • +
    + +

    The first three methods are strictly exclusive, with that exclusivity managed by the futures returned by didReceiveHead and +didReceiveBodyPart. What this means is that until the returned future is completed, none of these three methods will be called +again. This allows delegates to rate limit the server to a capacity it can manage. didFinishRequest does not return a future, +as we are expecting no more data from the server at this time.

    + +

    didReceiveError is somewhat special: it signals the end of this regime. didRecieveError is not exclusive: it may be called at +any time, even if a returned future is not yet completed. didReceiveError is terminal, meaning that once it has been called none +of these four methods will be called again. This can be used as a signal to abandon all outstanding work.

    +
    +

    Note

    + This delegate is strongly held by the HTTPTaskHandler + for the duration of the Request processing and will be + released together with the HTTPTaskHandler when channel is closed. + Users of the library are not required to keep a reference to the + object that implements this protocol, but may do so if needed. + +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public protocol HTTPClientResponseDelegate : AnyObject
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html b/docs/current/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html new file mode 100644 index 000000000..121c55bf0 --- /dev/null +++ b/docs/current/AsyncHTTPClient/Protocols/HTTPClientResponseDelegate.html @@ -0,0 +1,715 @@ + + + + HTTPClientResponseDelegate Protocol Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientResponseDelegate

+
+
+ +
public protocol HTTPClientResponseDelegate : AnyObject
+ +
+
+

HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed. +You can implement this protocol if you need fine-grained control over an HTTP request/response, for example, if you want to inspect the response +headers before deciding whether to accept a response body, or if you want to stream your request body. Pass an instance of your conforming +class to the HTTPClient.execute() method and this package will call each delegate method appropriately as the request takes place./

+

Backpressure

+ +

A HTTPClientResponseDelegate can be used to exert backpressure on the server response. This is achieved by way of the futures returned from +didReceiveHead and didReceiveBodyPart. The following functions are part of the “backpressure system” in the delegate:

+ +
    +
  • didReceiveHead
  • +
  • didReceiveBodyPart
  • +
  • didFinishRequest
  • +
  • didReceiveError
  • +
+ +

The first three methods are strictly exclusive, with that exclusivity managed by the futures returned by didReceiveHead and +didReceiveBodyPart. What this means is that until the returned future is completed, none of these three methods will be called +again. This allows delegates to rate limit the server to a capacity it can manage. didFinishRequest does not return a future, +as we are expecting no more data from the server at this time.

+ +

didReceiveError is somewhat special: it signals the end of this regime. didRecieveError is not exclusive: it may be called at +any time, even if a returned future is not yet completed. didReceiveError is terminal, meaning that once it has been called none +of these four methods will be called again. This can be used as a signal to abandon all outstanding work.

+
+

Note

+ This delegate is strongly held by the HTTPTaskHandler + for the duration of the Request processing and will be + released together with the HTTPTaskHandler when channel is closed. + Users of the library are not required to keep a reference to the + object that implements this protocol, but may do so if needed. + +
+ + +
+
+ +
+
+
+
    +
  • +
    + + + + Response + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    associatedtype Response
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequestHead(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when the request head is sent. Will be called once.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequestHead(task: HTTPClient.Task<Response>, _ head: HTTPRequestHead)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + head + + +
    +

    Request head.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequestPart(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when a part of the request body is sent. Could be called zero or more times.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequestPart(task: HTTPClient.Task<Response>, _ part: IOData)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + part + + +
    +

    Request body Part.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didSendRequest(task:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when the request is fully sent. Will be called once.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didSendRequest(task: HTTPClient.Task<Response>)
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveHead(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when response head is received. Will be called once. +You must return an EventLoopFuture<Void> that you complete when you have finished processing the body part. +You can create an already succeeded future by calling task.eventLoop.makeSucceededFuture(()).

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveHead(task: HTTPClient.Task<Response>, _ head: HTTPResponseHead) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + head + + +
    +

    Received reposonse head.

    +
    +
    +
    +
    +

    Return Value

    +

    EventLoopFuture that will be used for backpressure.

    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveBodyPart(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when part of a response body is received. Could be called zero or more times. +You must return an EventLoopFuture<Void> that you complete when you have finished processing the body part. +You can create an already succeeded future by calling task.eventLoop.makeSucceededFuture(()).

    + +

    This function will not be called until the future returned by didReceiveHead has completed.

    + +

    This function will not be called for subsequent body parts until the previous future returned by a +call to this function completes.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveBodyPart(task: HTTPClient.Task<Response>, _ buffer: ByteBuffer) -> EventLoopFuture<Void>
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + buffer + + +
    +

    Received body Part.

    +
    +
    +
    +
    +

    Return Value

    +

    EventLoopFuture that will be used for backpressure.

    +
    + +
    +
    +
  • +
  • +
    + + + + didReceiveError(task:_:) + + + Default implementation + +
    +
    +
    +
    +
    +
    +

    Called when error was thrown during request execution. Will be called zero or one time only. Request processing will be stopped after that.

    + +

    This function may be called at any time: it does not respect the backpressure exerted by didReceiveHead and didReceiveBodyPart. +All outstanding work may be cancelled when this is received. Once called, no further calls will be made to didReceiveHead, didReceiveBodyPart, +or didFinishRequest.

    + +
    +

    Default Implementation

    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didReceiveError(task: HTTPClient.Task<Response>, _ error: Error)
    + +
    +
    +
    +

    Parameters

    + + + + + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    + + error + + +
    +

    Error that occured during response processing.

    +
    +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Called when the complete HTTP request is finished. You must return an instance of your Response associated type. Will be called once, except if an error occurred.

    + +

    This function will not be called until all futures returned by didReceiveHead and didReceiveBodyPart have completed. Once called, +no further calls will be made to didReceiveHead, didReceiveBodyPart, or didReceiveError.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    func didFinishRequest(task: HTTPClient.Task<Response>) throws -> Response
    + +
    +
    +
    +

    Parameters

    + + + + + + + +
    + + task + + +
    +

    Current request context.

    +
    +
    +
    +
    +

    Return Value

    +

    Result of processing.

    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Structs.html b/docs/current/AsyncHTTPClient/Structs.html new file mode 100644 index 000000000..64d25dabb --- /dev/null +++ b/docs/current/AsyncHTTPClient/Structs.html @@ -0,0 +1,201 @@ + + + + Structures Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

Structures

+

The following structures are available globally.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + HTTPClientError + +
    +
    +
    +
    +
    +
    +

    Possible client errors.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct HTTPClientError : Error, Equatable, CustomStringConvertible
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/Structs/HTTPClientError.html b/docs/current/AsyncHTTPClient/Structs/HTTPClientError.html new file mode 100644 index 000000000..79f8a5158 --- /dev/null +++ b/docs/current/AsyncHTTPClient/Structs/HTTPClientError.html @@ -0,0 +1,899 @@ + + + + HTTPClientError Structure Reference + + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+

HTTPClientError

+
+
+ +
public struct HTTPClientError : Error, Equatable, CustomStringConvertible
+ +
+
+

Possible client errors.

+ + +
+
+ +
+
+
+
    +
  • +
    + + + + description + +
    +
    +
    +
    +
    +
    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public var description: String { get }
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + invalidURL + +
    +
    +
    +
    +
    +
    +

    URL provided is invalid.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let invalidURL: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + emptyHost + +
    +
    +
    +
    +
    +
    +

    URL does not contain host.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let emptyHost: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + missingSocketPath + +
    +
    +
    +
    +
    +
    +

    URL does not contain a socketPath as a host for http(s)+unix shemes.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let missingSocketPath: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + alreadyShutdown + +
    +
    +
    +
    +
    +
    +

    Client is shutdown and cannot be used for new requests.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let alreadyShutdown: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + emptyScheme + +
    +
    +
    +
    +
    +
    +

    URL does not contain scheme.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let emptyScheme: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + unsupportedScheme(_:) + +
    +
    +
    +
    +
    +
    +

    Provided URL scheme is not supported, supported schemes are: http and https

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func unsupportedScheme(_ scheme: String) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + readTimeout + +
    +
    +
    +
    +
    +
    +

    Request timed out.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let readTimeout: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Remote connection was closed unexpectedly.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let remoteConnectionClosed: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + cancelled + +
    +
    +
    +
    +
    +
    +

    Request was cancelled.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let cancelled: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Request contains invalid identity encoding.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let identityCodingIncorrectlyPresent: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Request contains multiple chunks definitions.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let chunkedSpecifiedMultipleTimes: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + invalidProxyResponse + +
    +
    +
    +
    +
    +
    +

    Proxy response was invalid.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let invalidProxyResponse: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + contentLengthMissing + +
    +
    +
    +
    +
    +
    +

    Request does not contain Content-Length header.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let contentLengthMissing: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Proxy Authentication Required.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let proxyAuthenticationRequired: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + redirectLimitReached + +
    +
    +
    +
    +
    +
    +

    Redirect Limit reached.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let redirectLimitReached: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + redirectCycleDetected + +
    +
    +
    +
    +
    +
    +

    Redirect Cycle detected.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let redirectCycleDetected: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + uncleanShutdown + +
    +
    +
    +
    +
    +
    +

    Unclean shutdown.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let uncleanShutdown: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + traceRequestWithBody + +
    +
    +
    +
    +
    +
    +

    A body was sent in a request with method TRACE.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let traceRequestWithBody: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Header field names contain invalid characters.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func invalidHeaderFieldNames(_ names: [String]) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    Header field values contain invalid characters.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static func invalidHeaderFieldValues(_ values: [String]) -> HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + bodyLengthMismatch + +
    +
    +
    +
    +
    +
    +

    Body length is not equal to Content-Length.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let bodyLengthMismatch: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + writeAfterRequestSent + +
    +
    +
    +
    +
    +
    +

    Body part was written after request was fully sent.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let writeAfterRequestSent: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
  • +
    + + + + incompatibleHeaders + +
    +
    +
    +
    +
    +
    +

    Incompatible headers specified, for example Transfer-Encoding and Content-Length.

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public static let incompatibleHeaders: HTTPClientError
    + +
    +
    + +
    +
    +
  • +
+
+
+
+ +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/badge.svg b/docs/current/AsyncHTTPClient/badge.svg new file mode 100644 index 000000000..b28dab66c --- /dev/null +++ b/docs/current/AsyncHTTPClient/badge.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + documentation + + + documentation + + + 89% + + + 89% + + + diff --git a/docs/current/AsyncHTTPClient/css/highlight.css b/docs/current/AsyncHTTPClient/css/highlight.css new file mode 100644 index 000000000..c170357ce --- /dev/null +++ b/docs/current/AsyncHTTPClient/css/highlight.css @@ -0,0 +1,202 @@ +/*! Jazzy - https://github.com/realm/jazzy + * Copyright Realm Inc. + * SPDX-License-Identifier: MIT + */ +/* Credit to https://gist.github.com/wataru420/2048287 */ +.highlight .c { + color: #999988; + font-style: italic; } + +.highlight .err { + color: #a61717; + background-color: #e3d2d2; } + +.highlight .k { + color: #000000; + font-weight: bold; } + +.highlight .o { + color: #000000; + font-weight: bold; } + +.highlight .cm { + color: #999988; + font-style: italic; } + +.highlight .cp { + color: #999999; + font-weight: bold; } + +.highlight .c1 { + color: #999988; + font-style: italic; } + +.highlight .cs { + color: #999999; + font-weight: bold; + font-style: italic; } + +.highlight .gd { + color: #000000; + background-color: #ffdddd; } + +.highlight .gd .x { + color: #000000; + background-color: #ffaaaa; } + +.highlight .ge { + color: #000000; + font-style: italic; } + +.highlight .gr { + color: #aa0000; } + +.highlight .gh { + color: #999999; } + +.highlight .gi { + color: #000000; + background-color: #ddffdd; } + +.highlight .gi .x { + color: #000000; + background-color: #aaffaa; } + +.highlight .go { + color: #888888; } + +.highlight .gp { + color: #555555; } + +.highlight .gs { + font-weight: bold; } + +.highlight .gu { + color: #aaaaaa; } + +.highlight .gt { + color: #aa0000; } + +.highlight .kc { + color: #000000; + font-weight: bold; } + +.highlight .kd { + color: #000000; + font-weight: bold; } + +.highlight .kp { + color: #000000; + font-weight: bold; } + +.highlight .kr { + color: #000000; + font-weight: bold; } + +.highlight .kt { + color: #445588; } + +.highlight .m { + color: #009999; } + +.highlight .s { + color: #d14; } + +.highlight .na { + color: #008080; } + +.highlight .nb { + color: #0086B3; } + +.highlight .nc { + color: #445588; + font-weight: bold; } + +.highlight .no { + color: #008080; } + +.highlight .ni { + color: #800080; } + +.highlight .ne { + color: #990000; + font-weight: bold; } + +.highlight .nf { + color: #990000; } + +.highlight .nn { + color: #555555; } + +.highlight .nt { + color: #000080; } + +.highlight .nv { + color: #008080; } + +.highlight .ow { + color: #000000; + font-weight: bold; } + +.highlight .w { + color: #bbbbbb; } + +.highlight .mf { + color: #009999; } + +.highlight .mh { + color: #009999; } + +.highlight .mi { + color: #009999; } + +.highlight .mo { + color: #009999; } + +.highlight .sb { + color: #d14; } + +.highlight .sc { + color: #d14; } + +.highlight .sd { + color: #d14; } + +.highlight .s2 { + color: #d14; } + +.highlight .se { + color: #d14; } + +.highlight .sh { + color: #d14; } + +.highlight .si { + color: #d14; } + +.highlight .sx { + color: #d14; } + +.highlight .sr { + color: #009926; } + +.highlight .s1 { + color: #d14; } + +.highlight .ss { + color: #990073; } + +.highlight .bp { + color: #999999; } + +.highlight .vc { + color: #008080; } + +.highlight .vg { + color: #008080; } + +.highlight .vi { + color: #008080; } + +.highlight .il { + color: #009999; } diff --git a/docs/current/AsyncHTTPClient/css/jazzy.css b/docs/current/AsyncHTTPClient/css/jazzy.css new file mode 100644 index 000000000..c7bb9fe22 --- /dev/null +++ b/docs/current/AsyncHTTPClient/css/jazzy.css @@ -0,0 +1,404 @@ +/*! Jazzy - https://github.com/realm/jazzy + * Copyright Realm Inc. + * SPDX-License-Identifier: MIT + */ +*, *:before, *:after { + box-sizing: inherit; } + +body { + margin: 0; + background: #fff; + color: #333; + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + letter-spacing: .2px; + -webkit-font-smoothing: antialiased; + box-sizing: border-box; } + +h1 { + font-size: 2rem; + font-weight: 700; + margin: 1.275em 0 0.6em; } + +h2 { + font-size: 1.75rem; + font-weight: 700; + margin: 1.275em 0 0.3em; } + +h3 { + font-size: 1.5rem; + font-weight: 700; + margin: 1em 0 0.3em; } + +h4 { + font-size: 1.25rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h5 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h6 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; + color: #777; } + +p { + margin: 0 0 1em; } + +ul, ol { + padding: 0 0 0 2em; + margin: 0 0 0.85em; } + +blockquote { + margin: 0 0 0.85em; + padding: 0 15px; + color: #858585; + border-left: 4px solid #e5e5e5; } + +img { + max-width: 100%; } + +a { + color: #4183c4; + text-decoration: none; } + a:hover, a:focus { + outline: 0; + text-decoration: underline; } + a.discouraged { + text-decoration: line-through; } + a.discouraged:hover, a.discouraged:focus { + text-decoration: underline line-through; } + +table { + background: #fff; + width: 100%; + border-collapse: collapse; + border-spacing: 0; + overflow: auto; + margin: 0 0 0.85em; } + +tr:nth-child(2n) { + background-color: #fbfbfb; } + +th, td { + padding: 6px 13px; + border: 1px solid #ddd; } + +hr { + height: 1px; + border: none; + background-color: #ddd; } + +pre { + margin: 0 0 1.275em; + padding: .85em 1em; + overflow: auto; + background: #f7f7f7; + font-size: .85em; + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +code { + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +.item-container p > code, .item-container li > code, .top-matter p > code, .top-matter li > code { + background: #f7f7f7; + padding: .2em; } + .item-container p > code:before, .item-container p > code:after, .item-container li > code:before, .item-container li > code:after, .top-matter p > code:before, .top-matter p > code:after, .top-matter li > code:before, .top-matter li > code:after { + letter-spacing: -.2em; + content: "\00a0"; } + +pre code { + padding: 0; + white-space: pre; } + +.content-wrapper { + display: flex; + flex-direction: column; } + @media (min-width: 768px) { + .content-wrapper { + flex-direction: row; } } +.header { + display: flex; + padding: 8px; + font-size: 0.875em; + background: #444; + color: #999; } + +.header-col { + margin: 0; + padding: 0 8px; } + +.header-col--primary { + flex: 1; } + +.header-link { + color: #fff; } + +.header-icon { + padding-right: 2px; + vertical-align: -3px; + height: 16px; } + +.breadcrumbs { + font-size: 0.875em; + padding: 8px 16px; + margin: 0; + background: #fbfbfb; + border-bottom: 1px solid #ddd; } + +.carat { + height: 10px; + margin: 0 5px; } + +.navigation { + order: 2; } + @media (min-width: 768px) { + .navigation { + order: 1; + width: 25%; + max-width: 300px; + padding-bottom: 64px; + overflow: hidden; + word-wrap: normal; + background: #fbfbfb; + border-right: 1px solid #ddd; } } +.nav-groups { + list-style-type: none; + padding-left: 0; } + +.nav-group-name { + border-bottom: 1px solid #ddd; + padding: 8px 0 8px 16px; } + +.nav-group-name-link { + color: #333; } + +.nav-group-tasks { + margin: 8px 0; + padding: 0 0 0 8px; } + +.nav-group-task { + font-size: 1em; + list-style-type: none; + white-space: nowrap; } + +.nav-group-task-link { + color: #808080; } + +.main-content { + order: 1; } + @media (min-width: 768px) { + .main-content { + order: 2; + flex: 1; + padding-bottom: 60px; } } +.section { + padding: 0 32px; + border-bottom: 1px solid #ddd; } + +.section-content { + max-width: 834px; + margin: 0 auto; + padding: 16px 0; } + +.section-name { + color: #666; + display: block; } + .section-name p { + margin-bottom: inherit; } + +.declaration .highlight { + overflow-x: initial; + padding: 8px 0; + margin: 0; + background-color: transparent; + border: none; } + +.task-group-section { + border-top: 1px solid #ddd; } + +.task-group { + padding-top: 0px; } + +.task-name-container a[name]:before { + content: ""; + display: block; } + +.section-name-container { + position: relative; } + .section-name-container .section-name-link { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + margin-bottom: 0; } + .section-name-container .section-name { + position: relative; + pointer-events: none; + z-index: 1; } + .section-name-container .section-name a { + pointer-events: auto; } + +.item-container { + padding: 0; } + +.item { + padding-top: 8px; + width: 100%; + list-style-type: none; } + .item a[name]:before { + content: ""; + display: block; } + .item .token, .item .direct-link { + display: inline-block; + text-indent: -20px; + padding-left: 3px; + margin-left: 20px; + font-size: 1rem; } + .item .declaration-note { + font-size: .85em; + color: #808080; + font-style: italic; } + +.pointer-container { + border-bottom: 1px solid #ddd; + left: -23px; + padding-bottom: 13px; + position: relative; + width: 110%; } + +.pointer { + left: 21px; + top: 7px; + display: block; + position: absolute; + width: 12px; + height: 12px; + border-left: 1px solid #ddd; + border-top: 1px solid #ddd; + background: #fff; + transform: rotate(45deg); } + +.height-container { + display: none; + position: relative; + width: 100%; + overflow: hidden; } + .height-container .section { + background: #fff; + border: 1px solid #ddd; + border-top-width: 0; + padding-top: 10px; + padding-bottom: 5px; + padding: 8px 16px; } + +.aside, .language { + padding: 6px 12px; + margin: 12px 0; + border-left: 5px solid #dddddd; + overflow-y: hidden; } + .aside .aside-title, .language .aside-title { + font-size: 9px; + letter-spacing: 2px; + text-transform: uppercase; + padding-bottom: 0; + margin: 0; + color: #aaa; + -webkit-user-select: none; } + .aside p:last-child, .language p:last-child { + margin-bottom: 0; } + +.language { + border-left: 5px solid #cde9f4; } + .language .aside-title { + color: #4183c4; } + +.aside-warning, .aside-deprecated, .aside-unavailable { + border-left: 5px solid #ff6666; } + .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { + color: #ff0000; } + +.graybox { + border-collapse: collapse; + width: 100%; } + .graybox p { + margin: 0; + word-break: break-word; + min-width: 50px; } + .graybox td { + border: 1px solid #ddd; + padding: 5px 25px 5px 10px; + vertical-align: middle; } + .graybox tr td:first-of-type { + text-align: right; + padding: 7px; + vertical-align: top; + word-break: normal; + width: 40px; } + +.slightly-smaller { + font-size: 0.9em; } + +.footer { + padding: 8px 16px; + background: #444; + color: #ddd; + font-size: 0.8em; } + .footer p { + margin: 8px 0; } + .footer a { + color: #fff; } + +html.dash .header, html.dash .breadcrumbs, html.dash .navigation { + display: none; } + +html.dash .height-container { + display: block; } + +form[role=search] input { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 24px; + padding: 0 10px; + margin: 0; + border: none; + border-radius: 1em; } + .loading form[role=search] input { + background: white url(http://github.com/img/spinner.gif) center right 4px no-repeat; } + +form[role=search] .tt-menu { + margin: 0; + min-width: 300px; + background: #fbfbfb; + color: #333; + border: 1px solid #ddd; } + +form[role=search] .tt-highlight { + font-weight: bold; } + +form[role=search] .tt-suggestion { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 0 8px; } + form[role=search] .tt-suggestion span { + display: table-cell; + white-space: nowrap; } + form[role=search] .tt-suggestion .doc-parent-name { + width: 100%; + text-align: right; + font-weight: normal; + font-size: 0.9em; + padding-left: 16px; } + +form[role=search] .tt-suggestion:hover, +form[role=search] .tt-suggestion.tt-cursor { + cursor: pointer; + background-color: #4183c4; + color: #fff; } + +form[role=search] .tt-suggestion:hover .doc-parent-name, +form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { + color: #fff; } diff --git a/docs/current/AsyncHTTPClient/img/carat.png b/docs/current/AsyncHTTPClient/img/carat.png new file mode 100755 index 000000000..29d2f7fd4 Binary files /dev/null and b/docs/current/AsyncHTTPClient/img/carat.png differ diff --git a/docs/current/AsyncHTTPClient/img/dash.png b/docs/current/AsyncHTTPClient/img/dash.png new file mode 100755 index 000000000..6f694c7a0 Binary files /dev/null and b/docs/current/AsyncHTTPClient/img/dash.png differ diff --git a/docs/current/AsyncHTTPClient/img/gh.png b/docs/current/AsyncHTTPClient/img/gh.png new file mode 100755 index 000000000..628da97c7 Binary files /dev/null and b/docs/current/AsyncHTTPClient/img/gh.png differ diff --git a/docs/current/AsyncHTTPClient/img/spinner.gif b/docs/current/AsyncHTTPClient/img/spinner.gif new file mode 100644 index 000000000..e3038d0a4 Binary files /dev/null and b/docs/current/AsyncHTTPClient/img/spinner.gif differ diff --git a/docs/current/AsyncHTTPClient/index.html b/docs/current/AsyncHTTPClient/index.html new file mode 100644 index 000000000..3762fc0e5 --- /dev/null +++ b/docs/current/AsyncHTTPClient/index.html @@ -0,0 +1,167 @@ + + + + AsyncHTTPClient Reference + + + + + + + + + + + + + + + +
+

+ + AsyncHTTPClient 1.4.1 Docs + + (89% documented) +

+ +
+
+ +
+
+ +

+ + GitHub + View on GitHub + +

+ +

+ + Dash + Install in Dash + +

+
+ + + +
+ +
+ +
+
+ +

AsyncHTTPClient Docs

+ +

AsyncHTTPClient is a Swift HTTP Client package.

+ +

To get started with AsyncHTTPClient, import AsyncHTTPClient. The +most important type is HTTPClient +which you can use to emit log messages.

+ +
+
+ + +
+
+ + + diff --git a/docs/current/AsyncHTTPClient/js/jazzy.js b/docs/current/AsyncHTTPClient/js/jazzy.js new file mode 100755 index 000000000..198441660 --- /dev/null +++ b/docs/current/AsyncHTTPClient/js/jazzy.js @@ -0,0 +1,74 @@ +// Jazzy - https://github.com/realm/jazzy +// Copyright Realm Inc. +// SPDX-License-Identifier: MIT + +window.jazzy = {'docset': false} +if (typeof window.dash != 'undefined') { + document.documentElement.className += ' dash' + window.jazzy.docset = true +} +if (navigator.userAgent.match(/xcode/i)) { + document.documentElement.className += ' xcode' + window.jazzy.docset = true +} + +function toggleItem($link, $content) { + var animationDuration = 300; + $link.toggleClass('token-open'); + $content.slideToggle(animationDuration); +} + +function itemLinkToContent($link) { + return $link.parent().parent().next(); +} + +// On doc load + hash-change, open any targetted item +function openCurrentItemIfClosed() { + if (window.jazzy.docset) { + return; + } + var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); + $content = itemLinkToContent($link); + if ($content.is(':hidden')) { + toggleItem($link, $content); + } +} + +$(openCurrentItemIfClosed); +$(window).on('hashchange', openCurrentItemIfClosed); + +// On item link ('token') click, toggle its discussion +$('.token').on('click', function(event) { + if (window.jazzy.docset) { + return; + } + var $link = $(this); + toggleItem($link, itemLinkToContent($link)); + + // Keeps the document from jumping to the hash. + var href = $link.attr('href'); + if (history.pushState) { + history.pushState({}, '', href); + } else { + location.hash = href; + } + event.preventDefault(); +}); + +// Clicks on links to the current, closed, item need to open the item +$("a:not('.token')").on('click', function() { + if (location == this.href) { + openCurrentItemIfClosed(); + } +}); + +// KaTeX rendering +if ("katex" in window) { + $($('.math').each( (_, element) => { + katex.render(element.textContent, element, { + displayMode: $(element).hasClass('m-block'), + throwOnError: false, + trust: true + }); + })) +} diff --git a/docs/current/AsyncHTTPClient/js/jazzy.search.js b/docs/current/AsyncHTTPClient/js/jazzy.search.js new file mode 100644 index 000000000..359cdbb8b --- /dev/null +++ b/docs/current/AsyncHTTPClient/js/jazzy.search.js @@ -0,0 +1,74 @@ +// Jazzy - https://github.com/realm/jazzy +// Copyright Realm Inc. +// SPDX-License-Identifier: MIT + +$(function(){ + var $typeahead = $('[data-typeahead]'); + var $form = $typeahead.parents('form'); + var searchURL = $form.attr('action'); + + function displayTemplate(result) { + return result.name; + } + + function suggestionTemplate(result) { + var t = '
'; + t += '' + result.name + ''; + if (result.parent_name) { + t += '' + result.parent_name + ''; + } + t += '
'; + return t; + } + + $typeahead.one('focus', function() { + $form.addClass('loading'); + + $.getJSON(searchURL).then(function(searchData) { + const searchIndex = lunr(function() { + this.ref('url'); + this.field('name'); + this.field('abstract'); + for (const [url, doc] of Object.entries(searchData)) { + this.add({url: url, name: doc.name, abstract: doc.abstract}); + } + }); + + $typeahead.typeahead( + { + highlight: true, + minLength: 3, + autoselect: true + }, + { + limit: 10, + display: displayTemplate, + templates: { suggestion: suggestionTemplate }, + source: function(query, sync) { + const lcSearch = query.toLowerCase(); + const results = searchIndex.query(function(q) { + q.term(lcSearch, { boost: 100 }); + q.term(lcSearch, { + boost: 10, + wildcard: lunr.Query.wildcard.TRAILING + }); + }).map(function(result) { + var doc = searchData[result.ref]; + doc.url = result.ref; + return doc; + }); + sync(results); + } + } + ); + $form.removeClass('loading'); + $typeahead.trigger('focus'); + }); + }); + + var baseURL = searchURL.slice(0, -"search.json".length); + + $typeahead.on('typeahead:select', function(e, result) { + window.location = baseURL + result.url; + }); +}); diff --git a/docs/current/AsyncHTTPClient/js/jquery.min.js b/docs/current/AsyncHTTPClient/js/jquery.min.js new file mode 100644 index 000000000..2c69bc908 --- /dev/null +++ b/docs/current/AsyncHTTPClient/js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.1 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,y=n.hasOwnProperty,a=y.toString,l=a.call(Object),v={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&v(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!y||!y.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ve(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ye(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ve(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],y=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||y.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||y.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||y.push(".#.+[+~]"),e.querySelectorAll("\\\f"),y.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),y=y.length&&new RegExp(y.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),v=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&v(p,e)?-1:t==C||t.ownerDocument==p&&v(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!y||!y.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),v.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",v.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",v.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),v.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(v.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return B(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=_e(v.pixelPosition,function(e,t){if(t)return t=Be(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return B(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 00){var c=e.utils.clone(r)||{};c.position=[a,l],c.index=s.length,s.push(new e.Token(i.slice(a,o),c))}a=o+1}}return s},e.tokenizer.separator=/[\s\-]+/,e.Pipeline=function(){this._stack=[]},e.Pipeline.registeredFunctions=Object.create(null),e.Pipeline.registerFunction=function(t,r){r in this.registeredFunctions&&e.utils.warn("Overwriting existing registered function: "+r),t.label=r,e.Pipeline.registeredFunctions[t.label]=t},e.Pipeline.warnIfFunctionNotRegistered=function(t){var r=t.label&&t.label in this.registeredFunctions;r||e.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",t)},e.Pipeline.load=function(t){var r=new e.Pipeline;return t.forEach(function(t){var i=e.Pipeline.registeredFunctions[t];if(!i)throw new Error("Cannot load unregistered function: "+t);r.add(i)}),r},e.Pipeline.prototype.add=function(){var t=Array.prototype.slice.call(arguments);t.forEach(function(t){e.Pipeline.warnIfFunctionNotRegistered(t),this._stack.push(t)},this)},e.Pipeline.prototype.after=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,r)},e.Pipeline.prototype.before=function(t,r){e.Pipeline.warnIfFunctionNotRegistered(r);var i=this._stack.indexOf(t);if(i==-1)throw new Error("Cannot find existingFn");this._stack.splice(i,0,r)},e.Pipeline.prototype.remove=function(e){var t=this._stack.indexOf(e);t!=-1&&this._stack.splice(t,1)},e.Pipeline.prototype.run=function(e){for(var t=this._stack.length,r=0;r1&&(se&&(r=n),s!=e);)i=r-t,n=t+Math.floor(i/2),s=this.elements[2*n];return s==e?2*n:s>e?2*n:sa?l+=2:o==a&&(t+=r[u+1]*i[l+1],u+=2,l+=2);return t},e.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},e.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),t=1,r=0;t0){var o,a=s.str.charAt(0);a in s.node.edges?o=s.node.edges[a]:(o=new e.TokenSet,s.node.edges[a]=o),1==s.str.length&&(o["final"]=!0),n.push({node:o,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(0!=s.editsRemaining){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new e.TokenSet;s.node.edges["*"]=u}if(0==s.str.length&&(u["final"]=!0),n.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&n.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),1==s.str.length&&(s.node["final"]=!0),s.str.length>=1){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new e.TokenSet;s.node.edges["*"]=l}1==s.str.length&&(l["final"]=!0),n.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var c,h=s.str.charAt(0),d=s.str.charAt(1);d in s.node.edges?c=s.node.edges[d]:(c=new e.TokenSet,s.node.edges[d]=c),1==s.str.length&&(c["final"]=!0),n.push({node:c,editsRemaining:s.editsRemaining-1,str:h+s.str.slice(2)})}}}return i},e.TokenSet.fromString=function(t){for(var r=new e.TokenSet,i=r,n=0,s=t.length;n=e;t--){var r=this.uncheckedNodes[t],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r["char"]]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}},e.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},e.Index.prototype.search=function(t){return this.query(function(r){var i=new e.QueryParser(t,r);i.parse()})},e.Index.prototype.query=function(t){for(var r=new e.Query(this.fields),i=Object.create(null),n=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),u=0;u1?this._b=1:this._b=e},e.Builder.prototype.k1=function(e){this._k1=e},e.Builder.prototype.add=function(t,r){var i=t[this._ref],n=Object.keys(this._fields);this._documents[i]=r||{},this.documentCount+=1;for(var s=0;s=this.length)return e.QueryLexer.EOS;var t=this.str.charAt(this.pos);return this.pos+=1,t},e.QueryLexer.prototype.width=function(){return this.pos-this.start},e.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},e.QueryLexer.prototype.backup=function(){this.pos-=1},e.QueryLexer.prototype.acceptDigitRun=function(){var t,r;do t=this.next(),r=t.charCodeAt(0);while(r>47&&r<58);t!=e.QueryLexer.EOS&&this.backup()},e.QueryLexer.prototype.more=function(){return this.pos1&&(t.backup(),t.emit(e.QueryLexer.TERM)),t.ignore(),t.more())return e.QueryLexer.lexText},e.QueryLexer.lexEditDistance=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.EDIT_DISTANCE),e.QueryLexer.lexText},e.QueryLexer.lexBoost=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.BOOST),e.QueryLexer.lexText},e.QueryLexer.lexEOS=function(t){t.width()>0&&t.emit(e.QueryLexer.TERM)},e.QueryLexer.termSeparator=e.tokenizer.separator,e.QueryLexer.lexText=function(t){for(;;){var r=t.next();if(r==e.QueryLexer.EOS)return e.QueryLexer.lexEOS;if(92!=r.charCodeAt(0)){if(":"==r)return e.QueryLexer.lexField;if("~"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexEditDistance;if("^"==r)return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexBoost;if("+"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if("-"==r&&1===t.width())return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if(r.match(e.QueryLexer.termSeparator))return e.QueryLexer.lexTerm}else t.escapeCharacter()}},e.QueryParser=function(t,r){this.lexer=new e.QueryLexer(t),this.query=r,this.currentClause={},this.lexemeIdx=0},e.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var t=e.QueryParser.parseClause;t;)t=t(this);return this.query},e.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},e.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},e.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},e.QueryParser.parseClause=function(t){var r=t.peekLexeme();if(void 0!=r)switch(r.type){case e.QueryLexer.PRESENCE:return e.QueryParser.parsePresence;case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expected either a field or a term, found "+r.type;throw r.str.length>=1&&(i+=" with value '"+r.str+"'"),new e.QueryParseError(i,r.start,r.end)}},e.QueryParser.parsePresence=function(t){var r=t.consumeLexeme();if(void 0!=r){switch(r.str){case"-":t.currentClause.presence=e.Query.presence.PROHIBITED;break;case"+":t.currentClause.presence=e.Query.presence.REQUIRED;break;default:var i="unrecognised presence operator'"+r.str+"'";throw new e.QueryParseError(i,r.start,r.end)}var n=t.peekLexeme();if(void 0==n){var i="expecting term or field, found nothing";throw new e.QueryParseError(i,r.start,r.end)}switch(n.type){case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var i="expecting term or field, found '"+n.type+"'";throw new e.QueryParseError(i,n.start,n.end)}}},e.QueryParser.parseField=function(t){var r=t.consumeLexeme();if(void 0!=r){if(t.query.allFields.indexOf(r.str)==-1){var i=t.query.allFields.map(function(e){return"'"+e+"'"}).join(", "),n="unrecognised field '"+r.str+"', possible fields: "+i;throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.fields=[r.str];var s=t.peekLexeme();if(void 0==s){var n="expecting term, found nothing";throw new e.QueryParseError(n,r.start,r.end)}switch(s.type){case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var n="expecting term, found '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseTerm=function(t){var r=t.consumeLexeme();if(void 0!=r){t.currentClause.term=r.str.toLowerCase(),r.str.indexOf("*")!=-1&&(t.currentClause.usePipeline=!1);var i=t.peekLexeme();if(void 0==i)return void t.nextClause();switch(i.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+i.type+"'";throw new e.QueryParseError(n,i.start,i.end)}}},e.QueryParser.parseEditDistance=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="edit distance must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.editDistance=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},e.QueryParser.parseBoost=function(t){var r=t.consumeLexeme();if(void 0!=r){var i=parseInt(r.str,10);if(isNaN(i)){var n="boost must be numeric";throw new e.QueryParseError(n,r.start,r.end)}t.currentClause.boost=i;var s=t.peekLexeme();if(void 0==s)return void t.nextClause();switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(n,s.start,s.end)}}},function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():e.lunr=t()}(this,function(){return e})}(); diff --git a/docs/current/AsyncHTTPClient/js/typeahead.jquery.js b/docs/current/AsyncHTTPClient/js/typeahead.jquery.js new file mode 100644 index 000000000..3a2d2ab03 --- /dev/null +++ b/docs/current/AsyncHTTPClient/js/typeahead.jquery.js @@ -0,0 +1,1694 @@ +/*! + * typeahead.js 1.3.1 + * https://github.com/corejavascript/typeahead.js + * Copyright 2013-2020 Twitter, Inc. and other contributors; Licensed MIT + */ + + +(function(root, factory) { + if (typeof define === "function" && define.amd) { + define([ "jquery" ], function(a0) { + return factory(a0); + }); + } else if (typeof module === "object" && module.exports) { + module.exports = factory(require("jquery")); + } else { + factory(root["jQuery"]); + } +})(this, function($) { + var _ = function() { + "use strict"; + return { + isMsie: function() { + return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false; + }, + isBlankString: function(str) { + return !str || /^\s*$/.test(str); + }, + escapeRegExChars: function(str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + }, + isString: function(obj) { + return typeof obj === "string"; + }, + isNumber: function(obj) { + return typeof obj === "number"; + }, + isArray: $.isArray, + isFunction: $.isFunction, + isObject: $.isPlainObject, + isUndefined: function(obj) { + return typeof obj === "undefined"; + }, + isElement: function(obj) { + return !!(obj && obj.nodeType === 1); + }, + isJQuery: function(obj) { + return obj instanceof $; + }, + toStr: function toStr(s) { + return _.isUndefined(s) || s === null ? "" : s + ""; + }, + bind: $.proxy, + each: function(collection, cb) { + $.each(collection, reverseArgs); + function reverseArgs(index, value) { + return cb(value, index); + } + }, + map: $.map, + filter: $.grep, + every: function(obj, test) { + var result = true; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (!(result = test.call(null, val, key, obj))) { + return false; + } + }); + return !!result; + }, + some: function(obj, test) { + var result = false; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (result = test.call(null, val, key, obj)) { + return false; + } + }); + return !!result; + }, + mixin: $.extend, + identity: function(x) { + return x; + }, + clone: function(obj) { + return $.extend(true, {}, obj); + }, + getIdGenerator: function() { + var counter = 0; + return function() { + return counter++; + }; + }, + templatify: function templatify(obj) { + return $.isFunction(obj) ? obj : template; + function template() { + return String(obj); + } + }, + defer: function(fn) { + setTimeout(fn, 0); + }, + debounce: function(func, wait, immediate) { + var timeout, result; + return function() { + var context = this, args = arguments, later, callNow; + later = function() { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + } + }; + callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + } + return result; + }; + }, + throttle: function(func, wait) { + var context, args, timeout, result, previous, later; + previous = 0; + later = function() { + previous = new Date(); + timeout = null; + result = func.apply(context, args); + }; + return function() { + var now = new Date(), remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + } else if (!timeout) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }, + stringify: function(val) { + return _.isString(val) ? val : JSON.stringify(val); + }, + guid: function() { + function _p8(s) { + var p = (Math.random().toString(16) + "000000000").substr(2, 8); + return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p; + } + return "tt-" + _p8() + _p8(true) + _p8(true) + _p8(); + }, + noop: function() {} + }; + }(); + var WWW = function() { + "use strict"; + var defaultClassNames = { + wrapper: "twitter-typeahead", + input: "tt-input", + hint: "tt-hint", + menu: "tt-menu", + dataset: "tt-dataset", + suggestion: "tt-suggestion", + selectable: "tt-selectable", + empty: "tt-empty", + open: "tt-open", + cursor: "tt-cursor", + highlight: "tt-highlight" + }; + return build; + function build(o) { + var www, classes; + classes = _.mixin({}, defaultClassNames, o); + www = { + css: buildCss(), + classes: classes, + html: buildHtml(classes), + selectors: buildSelectors(classes) + }; + return { + css: www.css, + html: www.html, + classes: www.classes, + selectors: www.selectors, + mixin: function(o) { + _.mixin(o, www); + } + }; + } + function buildHtml(c) { + return { + wrapper: '', + menu: '
' + }; + } + function buildSelectors(classes) { + var selectors = {}; + _.each(classes, function(v, k) { + selectors[k] = "." + v; + }); + return selectors; + } + function buildCss() { + var css = { + wrapper: { + position: "relative", + display: "inline-block" + }, + hint: { + position: "absolute", + top: "0", + left: "0", + borderColor: "transparent", + boxShadow: "none", + opacity: "1" + }, + input: { + position: "relative", + verticalAlign: "top", + backgroundColor: "transparent" + }, + inputWithNoHint: { + position: "relative", + verticalAlign: "top" + }, + menu: { + position: "absolute", + top: "100%", + left: "0", + zIndex: "100", + display: "none" + }, + ltr: { + left: "0", + right: "auto" + }, + rtl: { + left: "auto", + right: " 0" + } + }; + if (_.isMsie()) { + _.mixin(css.input, { + backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" + }); + } + return css; + } + }(); + var EventBus = function() { + "use strict"; + var namespace, deprecationMap; + namespace = "typeahead:"; + deprecationMap = { + render: "rendered", + cursorchange: "cursorchanged", + select: "selected", + autocomplete: "autocompleted" + }; + function EventBus(o) { + if (!o || !o.el) { + $.error("EventBus initialized without el"); + } + this.$el = $(o.el); + } + _.mixin(EventBus.prototype, { + _trigger: function(type, args) { + var $e = $.Event(namespace + type); + this.$el.trigger.call(this.$el, $e, args || []); + return $e; + }, + before: function(type) { + var args, $e; + args = [].slice.call(arguments, 1); + $e = this._trigger("before" + type, args); + return $e.isDefaultPrevented(); + }, + trigger: function(type) { + var deprecatedType; + this._trigger(type, [].slice.call(arguments, 1)); + if (deprecatedType = deprecationMap[type]) { + this._trigger(deprecatedType, [].slice.call(arguments, 1)); + } + } + }); + return EventBus; + }(); + var EventEmitter = function() { + "use strict"; + var splitter = /\s+/, nextTick = getNextTick(); + return { + onSync: onSync, + onAsync: onAsync, + off: off, + trigger: trigger + }; + function on(method, types, cb, context) { + var type; + if (!cb) { + return this; + } + types = types.split(splitter); + cb = context ? bindContext(cb, context) : cb; + this._callbacks = this._callbacks || {}; + while (type = types.shift()) { + this._callbacks[type] = this._callbacks[type] || { + sync: [], + async: [] + }; + this._callbacks[type][method].push(cb); + } + return this; + } + function onAsync(types, cb, context) { + return on.call(this, "async", types, cb, context); + } + function onSync(types, cb, context) { + return on.call(this, "sync", types, cb, context); + } + function off(types) { + var type; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + while (type = types.shift()) { + delete this._callbacks[type]; + } + return this; + } + function trigger(types) { + var type, callbacks, args, syncFlush, asyncFlush; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + args = [].slice.call(arguments, 1); + while ((type = types.shift()) && (callbacks = this._callbacks[type])) { + syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args)); + asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args)); + syncFlush() && nextTick(asyncFlush); + } + return this; + } + function getFlush(callbacks, context, args) { + return flush; + function flush() { + var cancelled; + for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) { + cancelled = callbacks[i].apply(context, args) === false; + } + return !cancelled; + } + } + function getNextTick() { + var nextTickFn; + if (window.setImmediate) { + nextTickFn = function nextTickSetImmediate(fn) { + setImmediate(function() { + fn(); + }); + }; + } else { + nextTickFn = function nextTickSetTimeout(fn) { + setTimeout(function() { + fn(); + }, 0); + }; + } + return nextTickFn; + } + function bindContext(fn, context) { + return fn.bind ? fn.bind(context) : function() { + fn.apply(context, [].slice.call(arguments, 0)); + }; + } + }(); + var highlight = function(doc) { + "use strict"; + var defaults = { + node: null, + pattern: null, + tagName: "strong", + className: null, + wordsOnly: false, + caseSensitive: false, + diacriticInsensitive: false + }; + var accented = { + A: "[AaªÀ-Åà-åĀ-ąǍǎȀ-ȃȦȧᴬᵃḀḁẚẠ-ảₐ℀℁℻⒜Ⓐⓐ㍱-㍴㎀-㎄㎈㎉㎩-㎯㏂㏊㏟㏿Aa]", + B: "[BbᴮᵇḂ-ḇℬ⒝Ⓑⓑ㍴㎅-㎇㏃㏈㏔㏝Bb]", + C: "[CcÇçĆ-čᶜ℀ℂ℃℅℆ℭⅭⅽ⒞Ⓒⓒ㍶㎈㎉㎝㎠㎤㏄-㏇Cc]", + D: "[DdĎďDŽ-džDZ-dzᴰᵈḊ-ḓⅅⅆⅮⅾ⒟Ⓓⓓ㋏㍲㍷-㍹㎗㎭-㎯㏅㏈Dd]", + E: "[EeÈ-Ëè-ëĒ-ěȄ-ȇȨȩᴱᵉḘ-ḛẸ-ẽₑ℡ℯℰⅇ⒠Ⓔⓔ㉐㋍㋎Ee]", + F: "[FfᶠḞḟ℉ℱ℻⒡Ⓕⓕ㎊-㎌㎙ff-fflFf]", + G: "[GgĜ-ģǦǧǴǵᴳᵍḠḡℊ⒢Ⓖⓖ㋌㋍㎇㎍-㎏㎓㎬㏆㏉㏒㏿Gg]", + H: "[HhĤĥȞȟʰᴴḢ-ḫẖℋ-ℎ⒣Ⓗⓗ㋌㍱㎐-㎔㏊㏋㏗Hh]", + I: "[IiÌ-Ïì-ïĨ-İIJijǏǐȈ-ȋᴵᵢḬḭỈ-ịⁱℐℑℹⅈⅠ-ⅣⅥ-ⅨⅪⅫⅰ-ⅳⅵ-ⅸⅺⅻ⒤Ⓘⓘ㍺㏌㏕fiffiIi]", + J: "[JjIJ-ĵLJ-njǰʲᴶⅉ⒥ⒿⓙⱼJj]", + K: "[KkĶķǨǩᴷᵏḰ-ḵK⒦Ⓚⓚ㎄㎅㎉㎏㎑㎘㎞㎢㎦㎪㎸㎾㏀㏆㏍-㏏Kk]", + L: "[LlĹ-ŀLJ-ljˡᴸḶḷḺ-ḽℒℓ℡Ⅼⅼ⒧Ⓛⓛ㋏㎈㎉㏐-㏓㏕㏖㏿flfflLl]", + M: "[MmᴹᵐḾ-ṃ℠™ℳⅯⅿ⒨Ⓜⓜ㍷-㍹㎃㎆㎎㎒㎖㎙-㎨㎫㎳㎷㎹㎽㎿㏁㏂㏎㏐㏔-㏖㏘㏙㏞㏟Mm]", + N: "[NnÑñŃ-ʼnNJ-njǸǹᴺṄ-ṋⁿℕ№⒩Ⓝⓝ㎁㎋㎚㎱㎵㎻㏌㏑Nn]", + O: "[OoºÒ-Öò-öŌ-őƠơǑǒǪǫȌ-ȏȮȯᴼᵒỌ-ỏₒ℅№ℴ⒪Ⓞⓞ㍵㏇㏒㏖Oo]", + P: "[PpᴾᵖṔ-ṗℙ⒫Ⓟⓟ㉐㍱㍶㎀㎊㎩-㎬㎰㎴㎺㏋㏗-㏚Pp]", + Q: "[Qqℚ⒬Ⓠⓠ㏃Qq]", + R: "[RrŔ-řȐ-ȓʳᴿᵣṘ-ṛṞṟ₨ℛ-ℝ⒭Ⓡⓡ㋍㍴㎭-㎯㏚㏛Rr]", + S: "[SsŚ-šſȘșˢṠ-ṣ₨℁℠⒮Ⓢⓢ㎧㎨㎮-㎳㏛㏜stSs]", + T: "[TtŢ-ťȚțᵀᵗṪ-ṱẗ℡™⒯Ⓣⓣ㉐㋏㎔㏏ſtstTt]", + U: "[UuÙ-Üù-üŨ-ųƯưǓǔȔ-ȗᵁᵘᵤṲ-ṷỤ-ủ℆⒰Ⓤⓤ㍳㍺Uu]", + V: "[VvᵛᵥṼ-ṿⅣ-Ⅷⅳ-ⅷ⒱Ⓥⓥⱽ㋎㍵㎴-㎹㏜㏞Vv]", + W: "[WwŴŵʷᵂẀ-ẉẘ⒲Ⓦⓦ㎺-㎿㏝Ww]", + X: "[XxˣẊ-ẍₓ℻Ⅸ-Ⅻⅸ-ⅻ⒳Ⓧⓧ㏓Xx]", + Y: "[YyÝýÿŶ-ŸȲȳʸẎẏẙỲ-ỹ⒴Ⓨⓨ㏉Yy]", + Z: "[ZzŹ-žDZ-dzᶻẐ-ẕℤℨ⒵Ⓩⓩ㎐-㎔Zz]" + }; + return function hightlight(o) { + var regex; + o = _.mixin({}, defaults, o); + if (!o.node || !o.pattern) { + return; + } + o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; + regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly, o.diacriticInsensitive); + traverse(o.node, hightlightTextNode); + function hightlightTextNode(textNode) { + var match, patternNode, wrapperNode; + if (match = regex.exec(textNode.data)) { + wrapperNode = doc.createElement(o.tagName); + o.className && (wrapperNode.className = o.className); + patternNode = textNode.splitText(match.index); + patternNode.splitText(match[0].length); + wrapperNode.appendChild(patternNode.cloneNode(true)); + textNode.parentNode.replaceChild(wrapperNode, patternNode); + } + return !!match; + } + function traverse(el, hightlightTextNode) { + var childNode, TEXT_NODE_TYPE = 3; + for (var i = 0; i < el.childNodes.length; i++) { + childNode = el.childNodes[i]; + if (childNode.nodeType === TEXT_NODE_TYPE) { + i += hightlightTextNode(childNode) ? 1 : 0; + } else { + traverse(childNode, hightlightTextNode); + } + } + } + }; + function accent_replacer(chr) { + return accented[chr.toUpperCase()] || chr; + } + function getRegex(patterns, caseSensitive, wordsOnly, diacriticInsensitive) { + var escapedPatterns = [], regexStr; + for (var i = 0, len = patterns.length; i < len; i++) { + var escapedWord = _.escapeRegExChars(patterns[i]); + if (diacriticInsensitive) { + escapedWord = escapedWord.replace(/\S/g, accent_replacer); + } + escapedPatterns.push(escapedWord); + } + regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")"; + return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); + } + }(window.document); + var Input = function() { + "use strict"; + var specialKeyCodeMap; + specialKeyCodeMap = { + 9: "tab", + 27: "esc", + 37: "left", + 39: "right", + 13: "enter", + 38: "up", + 40: "down" + }; + function Input(o, www) { + var id; + o = o || {}; + if (!o.input) { + $.error("input is missing"); + } + www.mixin(this); + this.$hint = $(o.hint); + this.$input = $(o.input); + this.$menu = $(o.menu); + id = this.$input.attr("id") || _.guid(); + this.$menu.attr("id", id + "_listbox"); + this.$hint.attr({ + "aria-hidden": true + }); + this.$input.attr({ + "aria-owns": id + "_listbox", + role: "combobox", + "aria-autocomplete": "list", + "aria-expanded": false + }); + this.query = this.$input.val(); + this.queryWhenFocused = this.hasFocus() ? this.query : null; + this.$overflowHelper = buildOverflowHelper(this.$input); + this._checkLanguageDirection(); + if (this.$hint.length === 0) { + this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; + } + this.onSync("cursorchange", this._updateDescendent); + } + Input.normalizeQuery = function(str) { + return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " "); + }; + _.mixin(Input.prototype, EventEmitter, { + _onBlur: function onBlur() { + this.resetInputValue(); + this.trigger("blurred"); + }, + _onFocus: function onFocus() { + this.queryWhenFocused = this.query; + this.trigger("focused"); + }, + _onKeydown: function onKeydown($e) { + var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; + this._managePreventDefault(keyName, $e); + if (keyName && this._shouldTrigger(keyName, $e)) { + this.trigger(keyName + "Keyed", $e); + } + }, + _onInput: function onInput() { + this._setQuery(this.getInputValue()); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + _managePreventDefault: function managePreventDefault(keyName, $e) { + var preventDefault; + switch (keyName) { + case "up": + case "down": + preventDefault = !withModifier($e); + break; + + default: + preventDefault = false; + } + preventDefault && $e.preventDefault(); + }, + _shouldTrigger: function shouldTrigger(keyName, $e) { + var trigger; + switch (keyName) { + case "tab": + trigger = !withModifier($e); + break; + + default: + trigger = true; + } + return trigger; + }, + _checkLanguageDirection: function checkLanguageDirection() { + var dir = (this.$input.css("direction") || "ltr").toLowerCase(); + if (this.dir !== dir) { + this.dir = dir; + this.$hint.attr("dir", dir); + this.trigger("langDirChanged", dir); + } + }, + _setQuery: function setQuery(val, silent) { + var areEquivalent, hasDifferentWhitespace; + areEquivalent = areQueriesEquivalent(val, this.query); + hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false; + this.query = val; + if (!silent && !areEquivalent) { + this.trigger("queryChanged", this.query); + } else if (!silent && hasDifferentWhitespace) { + this.trigger("whitespaceChanged", this.query); + } + }, + _updateDescendent: function updateDescendent(event, id) { + this.$input.attr("aria-activedescendant", id); + }, + bind: function() { + var that = this, onBlur, onFocus, onKeydown, onInput; + onBlur = _.bind(this._onBlur, this); + onFocus = _.bind(this._onFocus, this); + onKeydown = _.bind(this._onKeydown, this); + onInput = _.bind(this._onInput, this); + this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown); + if (!_.isMsie() || _.isMsie() > 9) { + this.$input.on("input.tt", onInput); + } else { + this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { + if (specialKeyCodeMap[$e.which || $e.keyCode]) { + return; + } + _.defer(_.bind(that._onInput, that, $e)); + }); + } + return this; + }, + focus: function focus() { + this.$input.focus(); + }, + blur: function blur() { + this.$input.blur(); + }, + getLangDir: function getLangDir() { + return this.dir; + }, + getQuery: function getQuery() { + return this.query || ""; + }, + setQuery: function setQuery(val, silent) { + this.setInputValue(val); + this._setQuery(val, silent); + }, + hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() { + return this.query !== this.queryWhenFocused; + }, + getInputValue: function getInputValue() { + return this.$input.val(); + }, + setInputValue: function setInputValue(value) { + this.$input.val(value); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + resetInputValue: function resetInputValue() { + this.setInputValue(this.query); + }, + getHint: function getHint() { + return this.$hint.val(); + }, + setHint: function setHint(value) { + this.$hint.val(value); + }, + clearHint: function clearHint() { + this.setHint(""); + }, + clearHintIfInvalid: function clearHintIfInvalid() { + var val, hint, valIsPrefixOfHint, isValid; + val = this.getInputValue(); + hint = this.getHint(); + valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; + isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow(); + !isValid && this.clearHint(); + }, + hasFocus: function hasFocus() { + return this.$input.is(":focus"); + }, + hasOverflow: function hasOverflow() { + var constraint = this.$input.width() - 2; + this.$overflowHelper.text(this.getInputValue()); + return this.$overflowHelper.width() >= constraint; + }, + isCursorAtEnd: function() { + var valueLength, selectionStart, range; + valueLength = this.$input.val().length; + selectionStart = this.$input[0].selectionStart; + if (_.isNumber(selectionStart)) { + return selectionStart === valueLength; + } else if (document.selection) { + range = document.selection.createRange(); + range.moveStart("character", -valueLength); + return valueLength === range.text.length; + } + return true; + }, + destroy: function destroy() { + this.$hint.off(".tt"); + this.$input.off(".tt"); + this.$overflowHelper.remove(); + this.$hint = this.$input = this.$overflowHelper = $("
"); + }, + setAriaExpanded: function setAriaExpanded(value) { + this.$input.attr("aria-expanded", value); + } + }); + return Input; + function buildOverflowHelper($input) { + return $('').css({ + position: "absolute", + visibility: "hidden", + whiteSpace: "pre", + fontFamily: $input.css("font-family"), + fontSize: $input.css("font-size"), + fontStyle: $input.css("font-style"), + fontVariant: $input.css("font-variant"), + fontWeight: $input.css("font-weight"), + wordSpacing: $input.css("word-spacing"), + letterSpacing: $input.css("letter-spacing"), + textIndent: $input.css("text-indent"), + textRendering: $input.css("text-rendering"), + textTransform: $input.css("text-transform") + }).insertAfter($input); + } + function areQueriesEquivalent(a, b) { + return Input.normalizeQuery(a) === Input.normalizeQuery(b); + } + function withModifier($e) { + return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; + } + }(); + var Dataset = function() { + "use strict"; + var keys, nameGenerator; + keys = { + dataset: "tt-selectable-dataset", + val: "tt-selectable-display", + obj: "tt-selectable-object" + }; + nameGenerator = _.getIdGenerator(); + function Dataset(o, www) { + o = o || {}; + o.templates = o.templates || {}; + o.templates.notFound = o.templates.notFound || o.templates.empty; + if (!o.source) { + $.error("missing source"); + } + if (!o.node) { + $.error("missing node"); + } + if (o.name && !isValidName(o.name)) { + $.error("invalid dataset name: " + o.name); + } + www.mixin(this); + this.highlight = !!o.highlight; + this.name = _.toStr(o.name || nameGenerator()); + this.limit = o.limit || 5; + this.displayFn = getDisplayFn(o.display || o.displayKey); + this.templates = getTemplates(o.templates, this.displayFn); + this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source; + this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async; + this._resetLastSuggestion(); + this.$el = $(o.node).attr("role", "presentation").addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); + } + Dataset.extractData = function extractData(el) { + var $el = $(el); + if ($el.data(keys.obj)) { + return { + dataset: $el.data(keys.dataset) || "", + val: $el.data(keys.val) || "", + obj: $el.data(keys.obj) || null + }; + } + return null; + }; + _.mixin(Dataset.prototype, EventEmitter, { + _overwrite: function overwrite(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (this.async && this.templates.pending) { + this._renderPending(query); + } else if (!this.async && this.templates.notFound) { + this._renderNotFound(query); + } else { + this._empty(); + } + this.trigger("rendered", suggestions, false, this.name); + }, + _append: function append(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length && this.$lastSuggestion.length) { + this._appendSuggestions(query, suggestions); + } else if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (!this.$lastSuggestion.length && this.templates.notFound) { + this._renderNotFound(query); + } + this.trigger("rendered", suggestions, true, this.name); + }, + _renderSuggestions: function renderSuggestions(query, suggestions) { + var $fragment; + $fragment = this._getSuggestionsFragment(query, suggestions); + this.$lastSuggestion = $fragment.children().last(); + this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions)); + }, + _appendSuggestions: function appendSuggestions(query, suggestions) { + var $fragment, $lastSuggestion; + $fragment = this._getSuggestionsFragment(query, suggestions); + $lastSuggestion = $fragment.children().last(); + this.$lastSuggestion.after($fragment); + this.$lastSuggestion = $lastSuggestion; + }, + _renderPending: function renderPending(query) { + var template = this.templates.pending; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _renderNotFound: function renderNotFound(query) { + var template = this.templates.notFound; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _empty: function empty() { + this.$el.empty(); + this._resetLastSuggestion(); + }, + _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) { + var that = this, fragment; + fragment = document.createDocumentFragment(); + _.each(suggestions, function getSuggestionNode(suggestion) { + var $el, context; + context = that._injectQuery(query, suggestion); + $el = $(that.templates.suggestion(context)).data(keys.dataset, that.name).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable); + fragment.appendChild($el[0]); + }); + this.highlight && highlight({ + className: this.classes.highlight, + node: fragment, + pattern: query + }); + return $(fragment); + }, + _getFooter: function getFooter(query, suggestions) { + return this.templates.footer ? this.templates.footer({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _getHeader: function getHeader(query, suggestions) { + return this.templates.header ? this.templates.header({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _resetLastSuggestion: function resetLastSuggestion() { + this.$lastSuggestion = $(); + }, + _injectQuery: function injectQuery(query, obj) { + return _.isObject(obj) ? _.mixin({ + _query: query + }, obj) : obj; + }, + update: function update(query) { + var that = this, canceled = false, syncCalled = false, rendered = 0; + this.cancel(); + this.cancel = function cancel() { + canceled = true; + that.cancel = $.noop; + that.async && that.trigger("asyncCanceled", query, that.name); + }; + this.source(query, sync, async); + !syncCalled && sync([]); + function sync(suggestions) { + if (syncCalled) { + return; + } + syncCalled = true; + suggestions = (suggestions || []).slice(0, that.limit); + rendered = suggestions.length; + that._overwrite(query, suggestions); + if (rendered < that.limit && that.async) { + that.trigger("asyncRequested", query, that.name); + } + } + function async(suggestions) { + suggestions = suggestions || []; + if (!canceled && rendered < that.limit) { + that.cancel = $.noop; + var idx = Math.abs(rendered - that.limit); + rendered += idx; + that._append(query, suggestions.slice(0, idx)); + that.async && that.trigger("asyncReceived", query, that.name); + } + } + }, + cancel: $.noop, + clear: function clear() { + this._empty(); + this.cancel(); + this.trigger("cleared"); + }, + isEmpty: function isEmpty() { + return this.$el.is(":empty"); + }, + destroy: function destroy() { + this.$el = $("
"); + } + }); + return Dataset; + function getDisplayFn(display) { + display = display || _.stringify; + return _.isFunction(display) ? display : displayFn; + function displayFn(obj) { + return obj[display]; + } + } + function getTemplates(templates, displayFn) { + return { + notFound: templates.notFound && _.templatify(templates.notFound), + pending: templates.pending && _.templatify(templates.pending), + header: templates.header && _.templatify(templates.header), + footer: templates.footer && _.templatify(templates.footer), + suggestion: templates.suggestion ? userSuggestionTemplate : suggestionTemplate + }; + function userSuggestionTemplate(context) { + var template = templates.suggestion; + return $(template(context)).attr("id", _.guid()); + } + function suggestionTemplate(context) { + return $('
').attr("id", _.guid()).text(displayFn(context)); + } + } + function isValidName(str) { + return /^[_a-zA-Z0-9-]+$/.test(str); + } + }(); + var Menu = function() { + "use strict"; + function Menu(o, www) { + var that = this; + o = o || {}; + if (!o.node) { + $.error("node is required"); + } + www.mixin(this); + this.$node = $(o.node); + this.query = null; + this.datasets = _.map(o.datasets, initializeDataset); + function initializeDataset(oDataset) { + var node = that.$node.find(oDataset.node).first(); + oDataset.node = node.length ? node : $("
").appendTo(that.$node); + return new Dataset(oDataset, www); + } + } + _.mixin(Menu.prototype, EventEmitter, { + _onSelectableClick: function onSelectableClick($e) { + this.trigger("selectableClicked", $($e.currentTarget)); + }, + _onRendered: function onRendered(type, dataset, suggestions, async) { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetRendered", dataset, suggestions, async); + }, + _onCleared: function onCleared() { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetCleared"); + }, + _propagate: function propagate() { + this.trigger.apply(this, arguments); + }, + _allDatasetsEmpty: function allDatasetsEmpty() { + return _.every(this.datasets, _.bind(function isDatasetEmpty(dataset) { + var isEmpty = dataset.isEmpty(); + this.$node.attr("aria-expanded", !isEmpty); + return isEmpty; + }, this)); + }, + _getSelectables: function getSelectables() { + return this.$node.find(this.selectors.selectable); + }, + _removeCursor: function _removeCursor() { + var $selectable = this.getActiveSelectable(); + $selectable && $selectable.removeClass(this.classes.cursor); + }, + _ensureVisible: function ensureVisible($el) { + var elTop, elBottom, nodeScrollTop, nodeHeight; + elTop = $el.position().top; + elBottom = elTop + $el.outerHeight(true); + nodeScrollTop = this.$node.scrollTop(); + nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10); + if (elTop < 0) { + this.$node.scrollTop(nodeScrollTop + elTop); + } else if (nodeHeight < elBottom) { + this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight)); + } + }, + bind: function() { + var that = this, onSelectableClick; + onSelectableClick = _.bind(this._onSelectableClick, this); + this.$node.on("click.tt", this.selectors.selectable, onSelectableClick); + this.$node.on("mouseover", this.selectors.selectable, function() { + that.setCursor($(this)); + }); + this.$node.on("mouseleave", function() { + that._removeCursor(); + }); + _.each(this.datasets, function(dataset) { + dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that); + }); + return this; + }, + isOpen: function isOpen() { + return this.$node.hasClass(this.classes.open); + }, + open: function open() { + this.$node.scrollTop(0); + this.$node.addClass(this.classes.open); + }, + close: function close() { + this.$node.attr("aria-expanded", false); + this.$node.removeClass(this.classes.open); + this._removeCursor(); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.attr("dir", dir); + }, + selectableRelativeToCursor: function selectableRelativeToCursor(delta) { + var $selectables, $oldCursor, oldIndex, newIndex; + $oldCursor = this.getActiveSelectable(); + $selectables = this._getSelectables(); + oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1; + newIndex = oldIndex + delta; + newIndex = (newIndex + 1) % ($selectables.length + 1) - 1; + newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex; + return newIndex === -1 ? null : $selectables.eq(newIndex); + }, + setCursor: function setCursor($selectable) { + this._removeCursor(); + if ($selectable = $selectable && $selectable.first()) { + $selectable.addClass(this.classes.cursor); + this._ensureVisible($selectable); + } + }, + getSelectableData: function getSelectableData($el) { + return $el && $el.length ? Dataset.extractData($el) : null; + }, + getActiveSelectable: function getActiveSelectable() { + var $selectable = this._getSelectables().filter(this.selectors.cursor).first(); + return $selectable.length ? $selectable : null; + }, + getTopSelectable: function getTopSelectable() { + var $selectable = this._getSelectables().first(); + return $selectable.length ? $selectable : null; + }, + update: function update(query) { + var isValidUpdate = query !== this.query; + if (isValidUpdate) { + this.query = query; + _.each(this.datasets, updateDataset); + } + return isValidUpdate; + function updateDataset(dataset) { + dataset.update(query); + } + }, + empty: function empty() { + _.each(this.datasets, clearDataset); + this.query = null; + this.$node.addClass(this.classes.empty); + function clearDataset(dataset) { + dataset.clear(); + } + }, + destroy: function destroy() { + this.$node.off(".tt"); + this.$node = $("
"); + _.each(this.datasets, destroyDataset); + function destroyDataset(dataset) { + dataset.destroy(); + } + } + }); + return Menu; + }(); + var Status = function() { + "use strict"; + function Status(options) { + this.$el = $("", { + role: "status", + "aria-live": "polite" + }).css({ + position: "absolute", + padding: "0", + border: "0", + height: "1px", + width: "1px", + "margin-bottom": "-1px", + "margin-right": "-1px", + overflow: "hidden", + clip: "rect(0 0 0 0)", + "white-space": "nowrap" + }); + options.$input.after(this.$el); + _.each(options.menu.datasets, _.bind(function(dataset) { + if (dataset.onSync) { + dataset.onSync("rendered", _.bind(this.update, this)); + dataset.onSync("cleared", _.bind(this.cleared, this)); + } + }, this)); + } + _.mixin(Status.prototype, { + update: function update(event, suggestions) { + var length = suggestions.length; + var words; + if (length === 1) { + words = { + result: "result", + is: "is" + }; + } else { + words = { + result: "results", + is: "are" + }; + } + this.$el.text(length + " " + words.result + " " + words.is + " available, use up and down arrow keys to navigate."); + }, + cleared: function() { + this.$el.text(""); + } + }); + return Status; + }(); + var DefaultMenu = function() { + "use strict"; + var s = Menu.prototype; + function DefaultMenu() { + Menu.apply(this, [].slice.call(arguments, 0)); + } + _.mixin(DefaultMenu.prototype, Menu.prototype, { + open: function open() { + !this._allDatasetsEmpty() && this._show(); + return s.open.apply(this, [].slice.call(arguments, 0)); + }, + close: function close() { + this._hide(); + return s.close.apply(this, [].slice.call(arguments, 0)); + }, + _onRendered: function onRendered() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onRendered.apply(this, [].slice.call(arguments, 0)); + }, + _onCleared: function onCleared() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onCleared.apply(this, [].slice.call(arguments, 0)); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl); + return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0)); + }, + _hide: function hide() { + this.$node.hide(); + }, + _show: function show() { + this.$node.css("display", "block"); + } + }); + return DefaultMenu; + }(); + var Typeahead = function() { + "use strict"; + function Typeahead(o, www) { + var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged; + o = o || {}; + if (!o.input) { + $.error("missing input"); + } + if (!o.menu) { + $.error("missing menu"); + } + if (!o.eventBus) { + $.error("missing event bus"); + } + www.mixin(this); + this.eventBus = o.eventBus; + this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; + this.input = o.input; + this.menu = o.menu; + this.enabled = true; + this.autoselect = !!o.autoselect; + this.active = false; + this.input.hasFocus() && this.activate(); + this.dir = this.input.getLangDir(); + this._hacks(); + this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this); + onFocused = c(this, "activate", "open", "_onFocused"); + onBlurred = c(this, "deactivate", "_onBlurred"); + onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed"); + onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed"); + onEscKeyed = c(this, "isActive", "_onEscKeyed"); + onUpKeyed = c(this, "isActive", "open", "_onUpKeyed"); + onDownKeyed = c(this, "isActive", "open", "_onDownKeyed"); + onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed"); + onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed"); + onQueryChanged = c(this, "_openIfActive", "_onQueryChanged"); + onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged"); + this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this); + } + _.mixin(Typeahead.prototype, { + _hacks: function hacks() { + var $input, $menu; + $input = this.input.$input || $("
"); + $menu = this.menu.$node || $("
"); + $input.on("blur.tt", function($e) { + var active, isActive, hasActive; + active = document.activeElement; + isActive = $menu.is(active); + hasActive = $menu.has(active).length > 0; + if (_.isMsie() && (isActive || hasActive)) { + $e.preventDefault(); + $e.stopImmediatePropagation(); + _.defer(function() { + $input.focus(); + }); + } + }); + $menu.on("mousedown.tt", function($e) { + $e.preventDefault(); + }); + }, + _onSelectableClicked: function onSelectableClicked(type, $el) { + this.select($el); + }, + _onDatasetCleared: function onDatasetCleared() { + this._updateHint(); + }, + _onDatasetRendered: function onDatasetRendered(type, suggestions, async, dataset) { + this._updateHint(); + if (this.autoselect) { + var cursorClass = this.selectors.cursor.substr(1); + this.menu.$node.find(this.selectors.suggestion).first().addClass(cursorClass); + } + this.eventBus.trigger("render", suggestions, async, dataset); + }, + _onAsyncRequested: function onAsyncRequested(type, dataset, query) { + this.eventBus.trigger("asyncrequest", query, dataset); + }, + _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) { + this.eventBus.trigger("asynccancel", query, dataset); + }, + _onAsyncReceived: function onAsyncReceived(type, dataset, query) { + this.eventBus.trigger("asyncreceive", query, dataset); + }, + _onFocused: function onFocused() { + this._minLengthMet() && this.menu.update(this.input.getQuery()); + }, + _onBlurred: function onBlurred() { + if (this.input.hasQueryChangedSinceLastFocus()) { + this.eventBus.trigger("change", this.input.getQuery()); + } + }, + _onEnterKeyed: function onEnterKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + if (this.select($selectable)) { + $e.preventDefault(); + $e.stopPropagation(); + } + } else if (this.autoselect) { + if (this.select(this.menu.getTopSelectable())) { + $e.preventDefault(); + $e.stopPropagation(); + } + } + }, + _onTabKeyed: function onTabKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + this.select($selectable) && $e.preventDefault(); + } else if (this.autoselect) { + if ($selectable = this.menu.getTopSelectable()) { + this.autocomplete($selectable) && $e.preventDefault(); + } + } + }, + _onEscKeyed: function onEscKeyed() { + this.close(); + }, + _onUpKeyed: function onUpKeyed() { + this.moveCursor(-1); + }, + _onDownKeyed: function onDownKeyed() { + this.moveCursor(+1); + }, + _onLeftKeyed: function onLeftKeyed() { + if (this.dir === "rtl" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); + } + }, + _onRightKeyed: function onRightKeyed() { + if (this.dir === "ltr" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); + } + }, + _onQueryChanged: function onQueryChanged(e, query) { + this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty(); + }, + _onWhitespaceChanged: function onWhitespaceChanged() { + this._updateHint(); + }, + _onLangDirChanged: function onLangDirChanged(e, dir) { + if (this.dir !== dir) { + this.dir = dir; + this.menu.setLanguageDirection(dir); + } + }, + _openIfActive: function openIfActive() { + this.isActive() && this.open(); + }, + _minLengthMet: function minLengthMet(query) { + query = _.isString(query) ? query : this.input.getQuery() || ""; + return query.length >= this.minLength; + }, + _updateHint: function updateHint() { + var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match; + $selectable = this.menu.getTopSelectable(); + data = this.menu.getSelectableData($selectable); + val = this.input.getInputValue(); + if (data && !_.isBlankString(val) && !this.input.hasOverflow()) { + query = Input.normalizeQuery(val); + escapedQuery = _.escapeRegExChars(query); + frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i"); + match = frontMatchRegEx.exec(data.val); + match && this.input.setHint(val + match[1]); + } else { + this.input.clearHint(); + } + }, + isEnabled: function isEnabled() { + return this.enabled; + }, + enable: function enable() { + this.enabled = true; + }, + disable: function disable() { + this.enabled = false; + }, + isActive: function isActive() { + return this.active; + }, + activate: function activate() { + if (this.isActive()) { + return true; + } else if (!this.isEnabled() || this.eventBus.before("active")) { + return false; + } else { + this.active = true; + this.eventBus.trigger("active"); + return true; + } + }, + deactivate: function deactivate() { + if (!this.isActive()) { + return true; + } else if (this.eventBus.before("idle")) { + return false; + } else { + this.active = false; + this.close(); + this.eventBus.trigger("idle"); + return true; + } + }, + isOpen: function isOpen() { + return this.menu.isOpen(); + }, + open: function open() { + if (!this.isOpen() && !this.eventBus.before("open")) { + this.input.setAriaExpanded(true); + this.menu.open(); + this._updateHint(); + this.eventBus.trigger("open"); + } + return this.isOpen(); + }, + close: function close() { + if (this.isOpen() && !this.eventBus.before("close")) { + this.input.setAriaExpanded(false); + this.menu.close(); + this.input.clearHint(); + this.input.resetInputValue(); + this.eventBus.trigger("close"); + } + return !this.isOpen(); + }, + setVal: function setVal(val) { + this.input.setQuery(_.toStr(val)); + }, + getVal: function getVal() { + return this.input.getQuery(); + }, + select: function select($selectable) { + var data = this.menu.getSelectableData($selectable); + if (data && !this.eventBus.before("select", data.obj, data.dataset)) { + this.input.setQuery(data.val, true); + this.eventBus.trigger("select", data.obj, data.dataset); + this.close(); + return true; + } + return false; + }, + autocomplete: function autocomplete($selectable) { + var query, data, isValid; + query = this.input.getQuery(); + data = this.menu.getSelectableData($selectable); + isValid = data && query !== data.val; + if (isValid && !this.eventBus.before("autocomplete", data.obj, data.dataset)) { + this.input.setQuery(data.val); + this.eventBus.trigger("autocomplete", data.obj, data.dataset); + return true; + } + return false; + }, + moveCursor: function moveCursor(delta) { + var query, $candidate, data, suggestion, datasetName, cancelMove, id; + query = this.input.getQuery(); + $candidate = this.menu.selectableRelativeToCursor(delta); + data = this.menu.getSelectableData($candidate); + suggestion = data ? data.obj : null; + datasetName = data ? data.dataset : null; + id = $candidate ? $candidate.attr("id") : null; + this.input.trigger("cursorchange", id); + cancelMove = this._minLengthMet() && this.menu.update(query); + if (!cancelMove && !this.eventBus.before("cursorchange", suggestion, datasetName)) { + this.menu.setCursor($candidate); + if (data) { + if (typeof data.val === "string") { + this.input.setInputValue(data.val); + } + } else { + this.input.resetInputValue(); + this._updateHint(); + } + this.eventBus.trigger("cursorchange", suggestion, datasetName); + return true; + } + return false; + }, + destroy: function destroy() { + this.input.destroy(); + this.menu.destroy(); + } + }); + return Typeahead; + function c(ctx) { + var methods = [].slice.call(arguments, 1); + return function() { + var args = [].slice.call(arguments); + _.each(methods, function(method) { + return ctx[method].apply(ctx, args); + }); + }; + } + }(); + (function() { + "use strict"; + var old, keys, methods; + old = $.fn.typeahead; + keys = { + www: "tt-www", + attrs: "tt-attrs", + typeahead: "tt-typeahead" + }; + methods = { + initialize: function initialize(o, datasets) { + var www; + datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); + o = o || {}; + www = WWW(o.classNames); + return this.each(attach); + function attach() { + var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, status, typeahead, MenuConstructor; + _.each(datasets, function(d) { + d.highlight = !!o.highlight; + }); + $input = $(this); + $wrapper = $(www.html.wrapper); + $hint = $elOrNull(o.hint); + $menu = $elOrNull(o.menu); + defaultHint = o.hint !== false && !$hint; + defaultMenu = o.menu !== false && !$menu; + defaultHint && ($hint = buildHintFromInput($input, www)); + defaultMenu && ($menu = $(www.html.menu).css(www.css.menu)); + $hint && $hint.val(""); + $input = prepInput($input, www); + if (defaultHint || defaultMenu) { + $wrapper.css(www.css.wrapper); + $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint); + $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null); + } + MenuConstructor = defaultMenu ? DefaultMenu : Menu; + eventBus = new EventBus({ + el: $input + }); + input = new Input({ + hint: $hint, + input: $input, + menu: $menu + }, www); + menu = new MenuConstructor({ + node: $menu, + datasets: datasets + }, www); + status = new Status({ + $input: $input, + menu: menu + }); + typeahead = new Typeahead({ + input: input, + menu: menu, + eventBus: eventBus, + minLength: o.minLength, + autoselect: o.autoselect + }, www); + $input.data(keys.www, www); + $input.data(keys.typeahead, typeahead); + } + }, + isEnabled: function isEnabled() { + var enabled; + ttEach(this.first(), function(t) { + enabled = t.isEnabled(); + }); + return enabled; + }, + enable: function enable() { + ttEach(this, function(t) { + t.enable(); + }); + return this; + }, + disable: function disable() { + ttEach(this, function(t) { + t.disable(); + }); + return this; + }, + isActive: function isActive() { + var active; + ttEach(this.first(), function(t) { + active = t.isActive(); + }); + return active; + }, + activate: function activate() { + ttEach(this, function(t) { + t.activate(); + }); + return this; + }, + deactivate: function deactivate() { + ttEach(this, function(t) { + t.deactivate(); + }); + return this; + }, + isOpen: function isOpen() { + var open; + ttEach(this.first(), function(t) { + open = t.isOpen(); + }); + return open; + }, + open: function open() { + ttEach(this, function(t) { + t.open(); + }); + return this; + }, + close: function close() { + ttEach(this, function(t) { + t.close(); + }); + return this; + }, + select: function select(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.select($el); + }); + return success; + }, + autocomplete: function autocomplete(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.autocomplete($el); + }); + return success; + }, + moveCursor: function moveCursoe(delta) { + var success = false; + ttEach(this.first(), function(t) { + success = t.moveCursor(delta); + }); + return success; + }, + val: function val(newVal) { + var query; + if (!arguments.length) { + ttEach(this.first(), function(t) { + query = t.getVal(); + }); + return query; + } else { + ttEach(this, function(t) { + t.setVal(_.toStr(newVal)); + }); + return this; + } + }, + destroy: function destroy() { + ttEach(this, function(typeahead, $input) { + revert($input); + typeahead.destroy(); + }); + return this; + } + }; + $.fn.typeahead = function(method) { + if (methods[method]) { + return methods[method].apply(this, [].slice.call(arguments, 1)); + } else { + return methods.initialize.apply(this, arguments); + } + }; + $.fn.typeahead.noConflict = function noConflict() { + $.fn.typeahead = old; + return this; + }; + function ttEach($els, fn) { + $els.each(function() { + var $input = $(this), typeahead; + (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input); + }); + } + function buildHintFromInput($input, www) { + return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop({ + readonly: true, + required: false + }).removeAttr("id name placeholder").removeClass("required").attr({ + spellcheck: "false", + tabindex: -1 + }); + } + function prepInput($input, www) { + $input.data(keys.attrs, { + dir: $input.attr("dir"), + autocomplete: $input.attr("autocomplete"), + spellcheck: $input.attr("spellcheck"), + style: $input.attr("style") + }); + $input.addClass(www.classes.input).attr({ + spellcheck: false + }); + try { + !$input.attr("dir") && $input.attr("dir", "auto"); + } catch (e) {} + return $input; + } + function getBackgroundStyles($el) { + return { + backgroundAttachment: $el.css("background-attachment"), + backgroundClip: $el.css("background-clip"), + backgroundColor: $el.css("background-color"), + backgroundImage: $el.css("background-image"), + backgroundOrigin: $el.css("background-origin"), + backgroundPosition: $el.css("background-position"), + backgroundRepeat: $el.css("background-repeat"), + backgroundSize: $el.css("background-size") + }; + } + function revert($input) { + var www, $wrapper; + www = $input.data(keys.www); + $wrapper = $input.parent().filter(www.selectors.wrapper); + _.each($input.data(keys.attrs), function(val, key) { + _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); + }); + $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input); + if ($wrapper.length) { + $input.detach().insertAfter($wrapper); + $wrapper.remove(); + } + } + function $elOrNull(obj) { + var isValid, $el; + isValid = _.isJQuery(obj) || _.isElement(obj); + $el = isValid ? $(obj).first() : []; + return $el.length ? $el : null; + } + })(); +}); \ No newline at end of file diff --git a/docs/current/AsyncHTTPClient/search.json b/docs/current/AsyncHTTPClient/search.json new file mode 100644 index 000000000..5cc1f48bf --- /dev/null +++ b/docs/current/AsyncHTTPClient/search.json @@ -0,0 +1 @@ +{"Structs/HTTPClientError.html#/s:s23CustomStringConvertibleP11descriptionSSvp":{"name":"description","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV10invalidURLACvpZ":{"name":"invalidURL","abstract":"

URL provided is invalid.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV9emptyHostACvpZ":{"name":"emptyHost","abstract":"

URL does not contain host.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV17missingSocketPathACvpZ":{"name":"missingSocketPath","abstract":"

URL does not contain a socketPath as a host for http(s)+unix shemes.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV15alreadyShutdownACvpZ":{"name":"alreadyShutdown","abstract":"

Client is shutdown and cannot be used for new requests.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV11emptySchemeACvpZ":{"name":"emptyScheme","abstract":"

URL does not contain scheme.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV17unsupportedSchemeyACSSFZ":{"name":"unsupportedScheme(_:)","abstract":"

Provided URL scheme is not supported, supported schemes are: http and https

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV11readTimeoutACvpZ":{"name":"readTimeout","abstract":"

Request timed out.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV22remoteConnectionClosedACvpZ":{"name":"remoteConnectionClosed","abstract":"

Remote connection was closed unexpectedly.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV9cancelledACvpZ":{"name":"cancelled","abstract":"

Request was cancelled.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV32identityCodingIncorrectlyPresentACvpZ":{"name":"identityCodingIncorrectlyPresent","abstract":"

Request contains invalid identity encoding.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV29chunkedSpecifiedMultipleTimesACvpZ":{"name":"chunkedSpecifiedMultipleTimes","abstract":"

Request contains multiple chunks definitions.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20invalidProxyResponseACvpZ":{"name":"invalidProxyResponse","abstract":"

Proxy response was invalid.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20contentLengthMissingACvpZ":{"name":"contentLengthMissing","abstract":"

Request does not contain Content-Length header.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV27proxyAuthenticationRequiredACvpZ":{"name":"proxyAuthenticationRequired","abstract":"

Proxy Authentication Required.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20redirectLimitReachedACvpZ":{"name":"redirectLimitReached","abstract":"

Redirect Limit reached.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV21redirectCycleDetectedACvpZ":{"name":"redirectCycleDetected","abstract":"

Redirect Cycle detected.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV15uncleanShutdownACvpZ":{"name":"uncleanShutdown","abstract":"

Unclean shutdown.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV20traceRequestWithBodyACvpZ":{"name":"traceRequestWithBody","abstract":"

A body was sent in a request with method TRACE.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV23invalidHeaderFieldNamesyACSaySSGFZ":{"name":"invalidHeaderFieldNames(_:)","abstract":"

Header field names contain invalid characters.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV24invalidHeaderFieldValuesyACSaySSGFZ":{"name":"invalidHeaderFieldValues(_:)","abstract":"

Header field values contain invalid characters.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV18bodyLengthMismatchACvpZ":{"name":"bodyLengthMismatch","abstract":"

Body length is not equal to Content-Length.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV21writeAfterRequestSentACvpZ":{"name":"writeAfterRequestSent","abstract":"

Body part was written after request was fully sent.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html#/s:15AsyncHTTPClient0B5ErrorV19incompatibleHeadersACvpZ":{"name":"incompatibleHeaders","abstract":"

Incompatible headers specified, for example Transfer-Encoding and Content-Length.

","parent_name":"HTTPClientError"},"Structs/HTTPClientError.html":{"name":"HTTPClientError","abstract":"

Possible client errors.

"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP0C0Qa":{"name":"Response","abstract":"

Undocumented

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didSendRequestHead4task_yAA0B0C4TaskCy_0C0QzG_8NIOHTTP1011HTTPRequestH0VtF":{"name":"didSendRequestHead(task:_:)","abstract":"

Called when the request head is sent. Will be called once.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didSendRequestPart4task_yAA0B0C4TaskCy_0C0QzG_7NIOCore6IODataOtF":{"name":"didSendRequestPart(task:_:)","abstract":"

Called when a part of the request body is sent. Could be called zero or more times.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didSendRequest4taskyAA0B0C4TaskCy_0C0QzG_tF":{"name":"didSendRequest(task:)","abstract":"

Called when the request is fully sent. Will be called once.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","abstract":"

Called when response head is received. Will be called once.","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","abstract":"

Called when part of a response body is received. Could be called zero or more times.","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","abstract":"

Called when error was thrown during request execution. Will be called zero or one time only. Request processing will be stopped after that.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","abstract":"

Called when the complete HTTP request is finished. You must return an instance of your Response associated type. Will be called once, except if an error occurred.

","parent_name":"HTTPClientResponseDelegate"},"Protocols/HTTPClientResponseDelegate.html":{"name":"HTTPClientResponseDelegate","abstract":"

HTTPClientResponseDelegate allows an implementation to receive notifications about request processing and to control how response parts are processed."},"Extensions/URL.html#/s:10Foundation3URLV15AsyncHTTPClientE21httpURLWithSocketPath3uriACSgSS_SStcfc":{"name":"init(httpURLWithSocketPath:uri:)","abstract":"

Initializes a newly created HTTP URL connecting to a unix domain socket path. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “http+unix” scheme.

","parent_name":"URL"},"Extensions/URL.html#/s:10Foundation3URLV15AsyncHTTPClientE22httpsURLWithSocketPath3uriACSgSS_SStcfc":{"name":"init(httpsURLWithSocketPath:uri:)","abstract":"

Initializes a newly created HTTPS URL connecting to a unix domain socket path over TLS. The socket path is encoded as the URL’s host, replacing percent encoding invalid path characters, and will use the “https+unix” scheme.

","parent_name":"URL"},"Extensions/URL.html":{"name":"URL"},"Extensions.html#/HTTP1ConnectionProvider":{"name":"HTTP1ConnectionProvider"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B15CopyingDelegateC8Responsea":{"name":"Response","abstract":"

Undocumented

","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B15CopyingDelegateC12chunkHandlerAC7NIOCore15EventLoopFutureCyytGAE10ByteBufferVc_tcfc":{"name":"init(chunkHandler:)","abstract":"

Undocumented

","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"HTTPClientCopyingDelegate"},"Classes/HTTPClientCopyingDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"HTTPClientCopyingDelegate"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient19ResponseAccumulatorC0C0a":{"name":"Response","abstract":"

Undocumented

","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient19ResponseAccumulatorC7requestAcA0B0C7RequestV_tcfc":{"name":"init(request:)","abstract":"

Undocumented

","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","parent_name":"ResponseAccumulator"},"Classes/ResponseAccumulator.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"ResponseAccumulator"},"Classes/HTTPClient/NWTLSError.html#/status":{"name":"status","abstract":"

TLS error status. List of TLS errors can be found in

","parent_name":"NWTLSError"},"Classes/HTTPClient/NWTLSError.html#/init(_:reason:)":{"name":"init(_:reason:)","abstract":"

initialise a NWTLSError

","parent_name":"NWTLSError"},"Classes/HTTPClient/NWTLSError.html#/description":{"name":"description","parent_name":"NWTLSError"},"Classes/HTTPClient/NWPOSIXError.html#/errorCode":{"name":"errorCode","abstract":"

POSIX error code (enum)

","parent_name":"NWPOSIXError"},"Classes/HTTPClient/NWPOSIXError.html#/init(_:reason:)":{"name":"init(_:reason:)","abstract":"

Initialise a NWPOSIXError

","parent_name":"NWPOSIXError"},"Classes/HTTPClient/NWPOSIXError.html#/description":{"name":"description","parent_name":"NWPOSIXError"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC9eventLoop7NIOCore05EventE0_pvp":{"name":"eventLoop","abstract":"

The EventLoop the delegate will be executed on.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC12futureResult7NIOCore15EventLoopFutureCyxGvp":{"name":"futureResult","abstract":"

EventLoopFuture for the response returned by this request.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC4waitxyKF":{"name":"wait()","abstract":"

Waits for execution of this request to complete.

","parent_name":"Task"},"Classes/HTTPClient/Task.html#/s:15AsyncHTTPClient0B0C4TaskC6cancelyyF":{"name":"cancel()","abstract":"

Cancels the request execution.

","parent_name":"Task"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV5basic8username8passwordAESS_SStFZ":{"name":"basic(username:password:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV5basic11credentialsAESS_tFZ":{"name":"basic(credentials:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV6bearer6tokensAESS_tFZ":{"name":"bearer(tokens:)","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Authorization.html#/s:15AsyncHTTPClient0B0C13AuthorizationV11headerValueSSvp":{"name":"headerValue","abstract":"

Undocumented

","parent_name":"Authorization"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6method8NIOHTTP110HTTPMethodOvp":{"name":"method","abstract":"

Request HTTP method, defaults to GET.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url10Foundation3URLVvp":{"name":"url","abstract":"

Remote URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6schemeSSvp":{"name":"scheme","abstract":"

Remote HTTP scheme, resolved from URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4hostSSvp":{"name":"host","abstract":"

Remote host, resolved from URL.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV7headers8NIOHTTP111HTTPHeadersVvp":{"name":"headers","abstract":"

Request custom HTTP Headers, defaults to no headers.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4bodyAC4BodyVSgvp":{"name":"body","abstract":"

Request body, defaults to no body.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV16tlsConfiguration6NIOSSL16TLSConfigurationVSgvp":{"name":"tlsConfiguration","abstract":"

Request-specific TLS configuration, defaults to no request-specific TLS configuration.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4bodyAESS_8NIOHTTP110HTTPMethodOAJ11HTTPHeadersVAC4BodyVSgtKcfc":{"name":"init(url:method:headers:body:)","abstract":"

Create HTTP request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4body16tlsConfigurationAESS_8NIOHTTP110HTTPMethodOAK11HTTPHeadersVAC4BodyVSg6NIOSSL16TLSConfigurationVSgtKcfc":{"name":"init(url:method:headers:body:tlsConfiguration:)","abstract":"

Create HTTP request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4bodyAE10Foundation3URLV_8NIOHTTP110HTTPMethodOAM11HTTPHeadersVAC4BodyVSgtKcfc":{"name":"init(url:method:headers:body:)","abstract":"

Create an HTTP Request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV3url6method7headers4body16tlsConfigurationAE10Foundation3URLV_8NIOHTTP110HTTPMethodOAN11HTTPHeadersVAC4BodyVSg6NIOSSL16TLSConfigurationVSgtKcfc":{"name":"init(url:method:headers:body:tlsConfiguration:)","abstract":"

Create an HTTP Request.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV6useTLSSbvp":{"name":"useTLS","abstract":"

Whether request will be executed using secure socket.

","parent_name":"Request"},"Classes/HTTPClient/Request.html#/s:15AsyncHTTPClient0B0C7RequestV4portSivp":{"name":"port","abstract":"

Resolved port.

","parent_name":"Request"},"Classes/HTTPClient/Body/StreamWriter.html#/s:15AsyncHTTPClient0B0C4BodyV12StreamWriterV7closureAG7NIOCore15EventLoopFutureCyytGAI6IODataOc_tcfc":{"name":"init(closure:)","abstract":"

Create new StreamWriter

","parent_name":"StreamWriter"},"Classes/HTTPClient/Body/StreamWriter.html#/s:15AsyncHTTPClient0B0C4BodyV12StreamWriterV5writey7NIOCore15EventLoopFutureCyytGAI6IODataOF":{"name":"write(_:)","abstract":"

Write data to server.

","parent_name":"StreamWriter"},"Classes/HTTPClient/Body/StreamWriter.html":{"name":"StreamWriter","abstract":"

Chunk provider.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6lengthSiSgvp":{"name":"length","abstract":"

Body size. Request validation will be failed with HTTPClientErrors.contentLengthMissing if nil,","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6streamy7NIOCore15EventLoopFutureCyytGAE12StreamWriterVcvp":{"name":"stream","abstract":"

Body chunk provider.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV10byteBufferyAE7NIOCore04ByteE0VFZ":{"name":"byteBuffer(_:)","abstract":"

Create and stream body using ByteBuffer.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6stream6length_AESiSg_7NIOCore15EventLoopFutureCyytGAE12StreamWriterVctFZ":{"name":"stream(length:_:)","abstract":"

Create and stream body using StreamWriter.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV4datayAE10Foundation4DataVFZ":{"name":"data(_:)","abstract":"

Create and stream body using Data.

","parent_name":"Body"},"Classes/HTTPClient/Body.html#/s:15AsyncHTTPClient0B0C4BodyV6stringyAESSFZ":{"name":"string(_:)","abstract":"

Create and stream body using String.

","parent_name":"Body"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4hostSSvp":{"name":"host","abstract":"

Remote host of the request.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV6status8NIOHTTP118HTTPResponseStatusOvp":{"name":"status","abstract":"

Response HTTP status.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7version8NIOHTTP111HTTPVersionVvp":{"name":"version","abstract":"

Response HTTP version.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7headers8NIOHTTP111HTTPHeadersVvp":{"name":"headers","abstract":"

Reponse HTTP headers.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4body7NIOCore10ByteBufferVSgvp":{"name":"body","abstract":"

Response body.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4host6status7headers4bodyAESS_8NIOHTTP118HTTPResponseStatusOAJ11HTTPHeadersV7NIOCore10ByteBufferVSgtcfc":{"name":"init(host:status:headers:body:)","abstract":"

Create HTTP Response.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV4host6status7version7headers4bodyAESS_8NIOHTTP118HTTPResponseStatusOAK11HTTPVersionVAK11HTTPHeadersV7NIOCore10ByteBufferVSgtcfc":{"name":"init(host:status:version:headers:body:)","abstract":"

Create HTTP Response.

","parent_name":"Response"},"Classes/HTTPClient/Response.html#/s:15AsyncHTTPClient0B0C8ResponseV7cookiesSayAC6CookieVGvp":{"name":"cookies","abstract":"

List of HTTP cookies returned by the server.

","parent_name":"Response"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4nameSSvp":{"name":"name","abstract":"

The name of the cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV5valueSSvp":{"name":"value","abstract":"

The cookie’s string value.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4pathSSvp":{"name":"path","abstract":"

The cookie’s path.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6domainSSSgvp":{"name":"domain","abstract":"

The domain of the cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV7expires10Foundation4DateVSgvp":{"name":"expires","abstract":"

The cookie’s expiration date.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6maxAgeSiSgvp":{"name":"maxAge","abstract":"

The cookie’s age in seconds.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV8httpOnlySbvp":{"name":"httpOnly","abstract":"

Whether the cookie should only be sent to HTTP servers.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6secureSbvp":{"name":"secure","abstract":"

Whether the cookie should only be sent over secure channels.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV6header13defaultDomainAESgSS_SStcfc":{"name":"init(header:defaultDomain:)","abstract":"

Create a Cookie by parsing a Set-Cookie header.

","parent_name":"Cookie"},"Classes/HTTPClient/Cookie.html#/s:15AsyncHTTPClient0B0C6CookieV4name5value4path6domain7expires6maxAge8httpOnly6secureAESS_S3SSg10Foundation4DateVSgSiSgS2btcfc":{"name":"init(name:value:path:domain:expires:maxAge:httpOnly:secure:)","abstract":"

Create HTTP cookie.

","parent_name":"Cookie"},"Classes/HTTPClient/Decompression.html#/s:15AsyncHTTPClient0B0C13DecompressionO8disabledyA2EmF":{"name":"disabled","abstract":"

Decompression is disabled.

","parent_name":"Decompression"},"Classes/HTTPClient/Decompression.html#/s:15AsyncHTTPClient0B0C13DecompressionO7enabledyAE18NIOHTTPCompression20NIOHTTPDecompressionO0C5LimitV_tcAEmF":{"name":"enabled(limit:)","abstract":"

Decompression is enabled.

","parent_name":"Decompression"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV11indifferentAEvpZ":{"name":"indifferent","abstract":"

Event Loop will be selected by the library.

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV8delegate2onAE7NIOCore0cD0_p_tFZ":{"name":"delegate(on:)","abstract":"

The delegate will be run on the specified EventLoop (and the Channel if possible).

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:15AsyncHTTPClient0B0C19EventLoopPreferenceV18delegateAndChannel2onAE7NIOCore0cD0_p_tFZ":{"name":"delegateAndChannel(on:)","abstract":"

The delegate and the Channel will be run on the specified EventLoop.

","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopPreference.html#/s:s23CustomStringConvertibleP11descriptionSSvp":{"name":"description","parent_name":"EventLoopPreference"},"Classes/HTTPClient/EventLoopGroupProvider.html#/s:15AsyncHTTPClient0B0C22EventLoopGroupProviderO6sharedyAE7NIOCore0cdE0_pcAEmF":{"name":"shared(_:)","abstract":"

EventLoopGroup will be provided by the user. Owner of this group is responsible for its lifecycle.

","parent_name":"EventLoopGroupProvider"},"Classes/HTTPClient/EventLoopGroupProvider.html#/s:15AsyncHTTPClient0B0C22EventLoopGroupProviderO9createNewyA2EmF":{"name":"createNew","abstract":"

EventLoopGroup will be created by the client. When syncShutdown is called, created EventLoopGroup will be shut down as well.

","parent_name":"EventLoopGroupProvider"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV4hostSSvp":{"name":"host","abstract":"

Specifies Proxy server host.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV4portSivp":{"name":"port","abstract":"

Specifies Proxy server port.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV13authorizationAC13AuthorizationVSgvp":{"name":"authorization","abstract":"

Specifies Proxy server authorization.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV6server4host4portAGSS_SitFZ":{"name":"server(host:port:)","abstract":"

Create a HTTP proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV6server4host4port13authorizationAGSS_SiAC13AuthorizationVSgtFZ":{"name":"server(host:port:authorization:)","abstract":"

Create a HTTP proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/Proxy.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5ProxyV11socksServer4host4portAGSS_SitFZ":{"name":"socksServer(host:port:)","abstract":"

Create a SOCKSv5 proxy.

","parent_name":"Proxy"},"Classes/HTTPClient/Configuration/ConnectionPool.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14ConnectionPoolV11idleTimeout7NIOCore10TimeAmountVvp":{"name":"idleTimeout","abstract":"

Undocumented

","parent_name":"ConnectionPool"},"Classes/HTTPClient/Configuration/ConnectionPool.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14ConnectionPoolV11idleTimeoutAG7NIOCore10TimeAmountV_tcfc":{"name":"init(idleTimeout:)","abstract":"

Undocumented

","parent_name":"ConnectionPool"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08RedirectC0V8disallowAGvpZ":{"name":"disallow","abstract":"

Redirects are not followed.

","parent_name":"RedirectConfiguration"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08RedirectC0V6follow3max11allowCyclesAGSi_SbtFZ":{"name":"follow(max:allowCycles:)","abstract":"

Redirects are followed with a specified limit.

","parent_name":"RedirectConfiguration"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV7connect7NIOCore10TimeAmountVSgvp":{"name":"connect","abstract":"

Specifies connect timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV4read7NIOCore10TimeAmountVSgvp":{"name":"read","abstract":"

Specifies read timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration/Timeout.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7TimeoutV7connect4readAG7NIOCore10TimeAmountVSg_AMtcfc":{"name":"init(connect:read:)","abstract":"

Create timeout.

","parent_name":"Timeout"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC06NIOSSL16TLSConfigurationVSgvp":{"name":"tlsConfiguration","abstract":"

TLS configuration, defaults to TLSConfiguration.forClient().

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV08redirectC0AE08RedirectC0Vvp":{"name":"redirectConfiguration","abstract":"

Enables following 3xx redirects automatically, defaults to RedirectConfiguration().

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV7timeoutAE7TimeoutVvp":{"name":"timeout","abstract":"

Default client timeout, defaults to no read timeout and 10 seconds connect timeout.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV14connectionPoolAE010ConnectionE0Vvp":{"name":"connectionPool","abstract":"

Connection pool configuration.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV5proxyAE5ProxyVSgvp":{"name":"proxy","abstract":"

Upstream proxy, defaults to no proxy.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV13decompressionAC13DecompressionOvp":{"name":"decompression","abstract":"

Enables automatic body decompression. Supported algorithms are gzip and deflate.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV24ignoreUncleanSSLShutdownSbvp":{"name":"ignoreUncleanSSLShutdown","abstract":"

Ignore TLS unclean shutdown error, defaults to false.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC008redirectC07timeout14connectionPool5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL16TLSConfigurationVSg_AE08RedirectC0VSgAE7TimeoutVAE010ConnectionH0VAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(tlsConfiguration:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV03tlsC008redirectC07timeout5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL16TLSConfigurationVSg_AE08RedirectC0VSgAE7TimeoutVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(tlsConfiguration:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout38maximumAllowedIdleTimeInConnectionPool5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutV7NIOCore0K6AmountVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:maximumAllowedIdleTimeInConnectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout14connectionPool5proxy24ignoreUncleanSSLShutdown13decompression24backgroundActivityLoggerAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutV7NIOCore10TimeAmountVAE5ProxyVSgSbAC13DecompressionO7Logging0Q0VSgtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:backgroundActivityLogger:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration.html#/s:15AsyncHTTPClient0B0C13ConfigurationV23certificateVerification08redirectC07timeout5proxy24ignoreUncleanSSLShutdown13decompressionAE6NIOSSL011CertificateE0O_AE08RedirectC0VSgAE7TimeoutVAE5ProxyVSgSbAC13DecompressionOtcfc":{"name":"init(certificateVerification:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)","abstract":"

Undocumented

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/Timeout.html":{"name":"Timeout","abstract":"

Timeout configuration.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/RedirectConfiguration.html":{"name":"RedirectConfiguration","abstract":"

Specifies redirect processing settings.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/ConnectionPool.html":{"name":"ConnectionPool","abstract":"

Connection pool configuration.

","parent_name":"Configuration"},"Classes/HTTPClient/Configuration/Proxy.html":{"name":"Proxy","abstract":"

Proxy server configuration","parent_name":"Configuration"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C14eventLoopGroup7NIOCore05EventdE0_pvp":{"name":"eventLoopGroup","abstract":"

Undocumented

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C22eventLoopGroupProvider13configurationA2C05EventdeF0O_AC13ConfigurationVtcfc":{"name":"init(eventLoopGroupProvider:configuration:)","abstract":"

Create an HTTPClient with specified EventLoopGroup provider and configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C22eventLoopGroupProvider13configuration24backgroundActivityLoggerA2C05EventdeF0O_AC13ConfigurationV7Logging0J0Vtcfc":{"name":"init(eventLoopGroupProvider:configuration:backgroundActivityLogger:)","abstract":"

Create an HTTPClient with specified EventLoopGroup provider and configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C12syncShutdownyyKF":{"name":"syncShutdown()","abstract":"

Shuts down the client and EventLoopGroup if it was created by the client.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C8shutdown5queue_y8Dispatch0E5QueueC_ys5Error_pSgctF":{"name":"shutdown(queue:_:)","abstract":"

Shuts down the client and event loop gracefully. This function is clearly an outlier in that it uses a completion","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3get3url8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AG11NIODeadlineVSgtF":{"name":"get(url:deadline:)","abstract":"

Execute GET request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3get3url8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"get(url:deadline:logger:)","abstract":"

Execute GET request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C4post3url4body8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"post(url:body:deadline:)","abstract":"

Execute POST request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C4post3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"post(url:body:deadline:logger:)","abstract":"

Execute POST request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C5patch3url4body8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"patch(url:body:deadline:)","abstract":"

Execute PATCH request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C5patch3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"patch(url:body:deadline:logger:)","abstract":"

Execute PATCH request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3put3url4body8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAH11NIODeadlineVSgtF":{"name":"put(url:body:deadline:)","abstract":"

Execute PUT request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C3put3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVtF":{"name":"put(url:body:deadline:logger:)","abstract":"

Execute PUT request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C6delete3url8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AG11NIODeadlineVSgtF":{"name":"delete(url:deadline:)","abstract":"

Execute DELETE request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C6delete3url8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGSS_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"delete(url:deadline:logger:)","abstract":"

Execute DELETE request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_3url4body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_SSAC4BodyVSgAI11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:url:body:deadline:logger:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_10socketPath03urlE04body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_S2SAC4BodyVSgAJ11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:socketPath:urlPath:body:deadline:logger:)","abstract":"

Execute arbitrary HTTP+UNIX request to a unix domain socket path, using the specified URL as the request to send to the server.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute_16secureSocketPath03urlF04body8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVG8NIOHTTP110HTTPMethodO_S2SAC4BodyVSgAJ11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(_:secureSocketPath:urlPath:body:deadline:logger:)","abstract":"

Execute arbitrary HTTPS+UNIX request to a unix domain socket path over TLS, using the specified URL as the request to send to the server.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8deadline7NIOCore15EventLoopFutureCyAC8ResponseVGAC7RequestV_AG11NIODeadlineVSgtF":{"name":"execute(request:deadline:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8deadline6logger7NIOCore15EventLoopFutureCyAC8ResponseVGAC7RequestV_AH11NIODeadlineVSg7Logging6LoggerVtF":{"name":"execute(request:deadline:logger:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request9eventLoop8deadline7NIOCore05EventF6FutureCyAC8ResponseVGAC7RequestV_AC0iF10PreferenceVAH11NIODeadlineVSgtF":{"name":"execute(request:eventLoop:deadline:)","abstract":"

Execute arbitrary HTTP request using specified URL.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request9eventLoop8deadline6logger7NIOCore05EventF6FutureCyAC8ResponseVGAC7RequestV_AC0jF10PreferenceVAI11NIODeadlineVSg7Logging6LoggerVSgtF":{"name":"execute(request:eventLoop:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate8deadlineAC4TaskCy_8ResponseQzGAC7RequestV_x7NIOCore11NIODeadlineVSgtAA0bH8DelegateRzlF":{"name":"execute(request:delegate:deadline:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate8deadline6loggerAC4TaskCy_8ResponseQzGAC7RequestV_x7NIOCore11NIODeadlineVSg7Logging6LoggerVtAA0bI8DelegateRzlF":{"name":"execute(request:delegate:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate9eventLoop8deadlineAC4TaskCy_8ResponseQzGAC7RequestV_xAC05EventG10PreferenceV7NIOCore11NIODeadlineVSgtAA0bJ8DelegateRzlF":{"name":"execute(request:delegate:eventLoop:deadline:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient.html#/s:15AsyncHTTPClient0B0C7execute7request8delegate9eventLoop8deadline6loggerAC4TaskCy_8ResponseQzGAC7RequestV_xAC05EventG10PreferenceV7NIOCore11NIODeadlineVSg7Logging6LoggerVSgtAA0bK8DelegateRzlF":{"name":"execute(request:delegate:eventLoop:deadline:logger:)","abstract":"

Execute arbitrary HTTP request and handle response processing using provided delegate.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Configuration.html":{"name":"Configuration","abstract":"

HTTPClient configuration.

","parent_name":"HTTPClient"},"Classes/HTTPClient/EventLoopGroupProvider.html":{"name":"EventLoopGroupProvider","abstract":"

Specifies how EventLoopGroup will be created and establishes lifecycle ownership.

","parent_name":"HTTPClient"},"Classes/HTTPClient/EventLoopPreference.html":{"name":"EventLoopPreference","abstract":"

Specifies how the library will treat event loop passed by the user.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Decompression.html":{"name":"Decompression","abstract":"

Specifies decompression settings.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Cookie.html":{"name":"Cookie","abstract":"

A representation of an HTTP cookie.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Response.html":{"name":"Response","abstract":"

Represent HTTP response.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Body.html":{"name":"Body","abstract":"

Represent request body.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Request.html":{"name":"Request","abstract":"

Represent HTTP request.

","parent_name":"HTTPClient"},"Classes/HTTPClient/Authorization.html":{"name":"Authorization","abstract":"

HTTP authentication

","parent_name":"HTTPClient"},"Classes/HTTPClient/Task.html":{"name":"Task","abstract":"

Response execution context. Will be created by the library and could be used for obtaining","parent_name":"HTTPClient"},"Classes/HTTPClient/NWPOSIXError.html":{"name":"NWPOSIXError","parent_name":"HTTPClient"},"Classes/HTTPClient/NWTLSError.html":{"name":"NWTLSError","parent_name":"HTTPClient"},"Classes/FileDownloadDelegate/Progress.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8ProgressV10totalBytesSiSgvp":{"name":"totalBytes","abstract":"

Undocumented

","parent_name":"Progress"},"Classes/FileDownloadDelegate/Progress.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8ProgressV13receivedBytesSivp":{"name":"receivedBytes","abstract":"

Undocumented

","parent_name":"Progress"},"Classes/FileDownloadDelegate/Progress.html":{"name":"Progress","abstract":"

The response type for this delegate: the total count of bytes as reported by the response","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient20FileDownloadDelegateC8Responsea":{"name":"Response","abstract":"

Undocumented

","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient20FileDownloadDelegateC4path4pool10reportHead0H8ProgressACSS_8NIOPosix13NIOThreadPoolCy8NIOHTTP1012HTTPResponseI0VcSgyAC0J0VcSgtKcfc":{"name":"init(path:pool:reportHead:reportProgress:)","abstract":"

Initializes a new file download delegate.

","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP14didReceiveHead4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_8NIOHTTP1012HTTPResponseG0VtF":{"name":"didReceiveHead(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP18didReceiveBodyPart4task_7NIOCore15EventLoopFutureCyytGAA0B0C4TaskCy_0C0QzG_AF10ByteBufferVtF":{"name":"didReceiveBodyPart(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP15didReceiveError4task_yAA0B0C4TaskCy_0C0QzG_s0G0_ptF":{"name":"didReceiveError(task:_:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html#/s:15AsyncHTTPClient0B16ResponseDelegateP16didFinishRequest4task0C0QzAA0B0C4TaskCy_AGG_tKF":{"name":"didFinishRequest(task:)","parent_name":"FileDownloadDelegate"},"Classes/FileDownloadDelegate.html":{"name":"FileDownloadDelegate","abstract":"

Handles a streaming download to a given file path, allowing headers and progress to be reported.

"},"Classes/HTTPClient.html":{"name":"HTTPClient","abstract":"

HTTPClient class provides API for request execution.

"},"Classes/ResponseAccumulator.html":{"name":"ResponseAccumulator","abstract":"

Undocumented

"},"Classes/HTTPClientCopyingDelegate.html":{"name":"HTTPClientCopyingDelegate","abstract":"

Undocumented

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Extensions.html":{"name":"Extensions","abstract":"

The following extensions are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"},"Structs.html":{"name":"Structures","abstract":"

The following structures are available globally.

"}} \ No newline at end of file diff --git a/docs/current/AsyncHTTPClient/undocumented.json b/docs/current/AsyncHTTPClient/undocumented.json new file mode 100644 index 000000000..daaebc5e9 --- /dev/null +++ b/docs/current/AsyncHTTPClient/undocumented.json @@ -0,0 +1,159 @@ +{ + "warnings": [ + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 23, + "symbol": "FileDownloadDelegate.Progress.totalBytes", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 24, + "symbol": "FileDownloadDelegate.Progress.receivedBytes", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/FileDownloadDelegate.swift", + "line": 29, + "symbol": "FileDownloadDelegate.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 67, + "symbol": "HTTPClient.eventLoopGroup", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 664, + "symbol": "HTTPClient.Configuration.init(tlsConfiguration:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 680, + "symbol": "HTTPClient.Configuration.init(tlsConfiguration:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 697, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:maximumAllowedIdleTimeInConnectionPool:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 713, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:connectionPool:proxy:ignoreUncleanSSLShutdown:decompression:backgroundActivityLogger:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 730, + "symbol": "HTTPClient.Configuration.init(certificateVerification:redirectConfiguration:timeout:proxy:ignoreUncleanSSLShutdown:decompression:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 878, + "symbol": "HTTPClient.Configuration.ConnectionPool.idleTimeout", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPClient.swift", + "line": 880, + "symbol": "HTTPClient.Configuration.ConnectionPool.init(idleTimeout:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 357, + "symbol": "HTTPClient.Authorization.basic(username:password:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 361, + "symbol": "HTTPClient.Authorization.basic(credentials:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 365, + "symbol": "HTTPClient.Authorization.bearer(tokens:)", + "symbol_kind": "source.lang.swift.decl.function.method.static", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 369, + "symbol": "HTTPClient.Authorization.headerValue", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 380, + "symbol": "ResponseAccumulator", + "symbol_kind": "source.lang.swift.decl.class", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 381, + "symbol": "ResponseAccumulator.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 394, + "symbol": "ResponseAccumulator.init(request:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/HTTPHandler.swift", + "line": 487, + "symbol": "HTTPClientResponseDelegate.Response", + "symbol_kind": "source.lang.swift.decl.associatedtype", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 38, + "symbol": "HTTPClientCopyingDelegate", + "symbol_kind": "source.lang.swift.decl.class", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 39, + "symbol": "HTTPClientCopyingDelegate.Response", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/code/Sources/AsyncHTTPClient/Utils.swift", + "line": 43, + "symbol": "HTTPClientCopyingDelegate.init(chunkHandler:)", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + } + ], + "source_directory": "/code" +} \ No newline at end of file diff --git a/docs/logging-design.md b/docs/logging-design.md deleted file mode 100644 index 02ef721fa..000000000 --- a/docs/logging-design.md +++ /dev/null @@ -1,70 +0,0 @@ -# Design of the way AsyncHTTPClient logs - -
- The logging is strictly separated between request activity & background activity. - AsyncHTTPClient is very much a request-driven library. Almost all work happens when you invoke a request, say `httpClient.get(someURL)`. To preserve the metadata you may have attached to your current `Logger`, we accept a `logger: Logger` parameter on each request. For example to so a `GET` request with logging use the following code. - -```swift -httpClient.get(someURL, logger: myLogger) -``` - - Apart from the request-driven work, AsyncHTTPClient does do some very limited amount of background work, for example expiring connections that stayed unused in the connection pool for too long. Logs associated with the activity from background tasks can be seen only if you attach a `Logger` in `HTTPClient`'s initialiser like below. - -```swift -HTTPClient(eventLoopGroupProvider: .shared(group), - backgroundActivityLogger: self.myBackgroundLogger) -``` - -The rationale for the strict separation is the correct propagation of the `Logger`'s `metadata`. You are likely to attach request specific information to a `Logger` before passing it to one of AsyncHTTPClient's request methods. This metadata will then be correctly attached to all log messages that occur from AsyncHTTPClient processing this request. - -If AsyncHTTPClient does some work in the background (like closing a connection that was long idle) however you likely do _not_ want the request-specific information from some previous request to be attached to those messages. Therefore, those messages get logged with the `backgroundActivityLogger` passed to HTTPClient's initialiser. -
-
- Unless you explicitly pass AsyncHTTPClient a `Logger` instance, nothing is ever logged. - AsyncHTTPClient is useful in many places where you wouldn't want to log, for example a command line HTTP client. Also, we do not want to change its default behaviour in a minor release. -
-
- Nothing is logged at level `info` or higher, unless something is really wrong that cannot be communicated through the API. - Fundamentally, AsyncHTTPClient performs a simple task, it makes a HTTP request and communicates the outcome back via its API. In normal usage, we would not expect people to want AsyncHTTPClient to log. In certain scenarios, for example when debugging why a request takes longer than expected it may however be useful to get information about AsyncHTTPClient's connection pool. That is when enabling logging may become useful. -
-
- Each request will get a globally unique request ID (`ahc-request-id`) that will be attached (as metadata) to each log message relevant to a request. - When many concurrent requests are active, it can be challenging to figure out which log message is associated with which request. To facilitate this task, AsyncHTTPClient will add a metadata field `ahc-request-id` to each log message so you can first find the request ID that is causing issues and then filter only messages with that ID. -
-
- Your `Logger` metadata is preserved. - AsyncHTTPClient accepts a `Logger` on every request method. This means that all the metadata you have attached, will be present on log messages issued by AsyncHTTPClient. - - For example, if you attach `["my-system-req-uuid": "84B453E0-0DFD-4B4B-BF22-3434812C9015"]` and then do two requests using AsyncHTTPClient, both of those requests will carry `"my-system-req-uuid` as well as AsyncHTTPClient's `ahc-request-id`. This allows you to filter all HTTP request made from one of your system's requests whilst still disambiguating the HTTP requests (they will have different `ahc-request-id`s. -
-
- Instead of accepting one `Logger` instance per `HTTPClient` instance, each request method can accept a `Logger`. - This allows AsyncHTTPClient to preserve your metadata and add its own metadata such as `ahc-request-id`. -
-
- All logs use the [structured logging](https://www.sumologic.com/glossary/structured-logging/) pattern, i.e. only static log messages and accompanying key/value metadata are used. - None of the log messages issued by AsyncHTTPClient will use String interpolation which means they will always be the exact same message. - - For example when AsyncHTTPClient wants to tell you it got an actual network connection to perform a request on, it will give the logger the following pieces of information: - - - message: `got connection for request` - - metadata (the values are example): - - `ahc-request-id`: `0` - - `ahc-connection`: `SocketChannel { BaseSocket { fd=15 }, active = true, localAddress = Optional([IPv4]127.0.0.1/127.0.0.1:54459), remoteAddress = Optional([IPv4]127.0.0.1/127.0.0.1:54457) }` - - As you can see above, the log message doesn't actually contain the request or the network connection. Both of those pieces of information are in the `metadata`. - - The rationale is that many people use log aggregation systems where it is very useful to aggregate, search and group by log message, or specific metadata values. This is greatly simplified by using a constant string (relatively stable) string and explicitly marked metadata values which make it easy to filter by. -
-
- `debug` should be enough to diagnose most problems but information that can be correlated is usually skipped. - When crafting log messages, it's often hard to strike a balance between logging everything and logging just enough. A rule of thumb is that you have to assume someone may be running with `logLevel = .debug` in production. So it can't be too much. Yet `.trace` can log everything you would need to know when debugging a tricky implementation issue. We assume nobody is running in production with `logLevel = .trace`. - - The problem with logging everything is that logging itself becomes very slow. We want logging in `debug` level to still be reasonably performant and therefore avoid logging information that can be correlated from other log messages. - - For example, AsyncHTTPClient may tell you in two log messages that it `got a connection` (from the connection pool) and a little later that it's `parking connection` (in the connection pool). Just like all messages, both of them will have an associated `ahc-request-id` which makes it possible to correlate the two log messages. The message that logs that we actually got a network connection will also include information about this network connection. The message that we're now parking the connection however _will not_. The information which connection is being parked can be found by filtering all other log messages with the same `ahc-request-id`. -
-
- In `trace`, AsyncHTTPClient may log _a lot_. - In the `.trace` log level, AsyncHTTPClient basically logs all the information that it has handily available. The frugality considerations we take in `.debug` do not apply here. We just want to log as much information as possible. This is useful almost exclusively for local debugging and should almost certainly not be sent into a log aggregation system where the information might be persisted for a long time. This also means, handing AsyncHTTPClient a logger in `logLevel = .trace` may have a fairly serious performance impact. -
diff --git a/index.html b/index.html new file mode 100644 index 000000000..b150bdf45 --- /dev/null +++ b/index.html @@ -0,0 +1 @@ +