Files
async-http-client/Sources/AsyncHTTPClient/AsyncAwait/HTTPClientResponse.swift
T
David Nadoba d372bdc213 Make async/await available on older Apple Platforms (#527)
### Motivation
With Xcode 13.2, and therefore Swift 5.5.2, Swift Concurrecy is supported on older Apple OSs. async/await suport will no longer be available on Swift before `5.5.2` but this isn't a breaking change because we have not yet made anything of it public.

### Changes
- replace all `#if compiler(>=5.5) && canImport(_Concurrency)` with `#if compiler(>=5.5.2) && canImport(_Concurrency)`
- replace all `available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)` with `available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)`
2021-12-17 16:08:24 +01:00

120 lines
3.2 KiB
Swift

//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#if compiler(>=5.5.2) && canImport(_Concurrency)
import NIOCore
import NIOHTTP1
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
struct HTTPClientResponse {
var version: HTTPVersion
var status: HTTPResponseStatus
var headers: HTTPHeaders
var body: Body
struct Body {
private let bag: Transaction
private let reference: ResponseRef
fileprivate init(_ transaction: Transaction) {
self.bag = transaction
self.reference = ResponseRef(transaction: transaction)
}
}
init(
bag: Transaction,
version: HTTPVersion,
status: HTTPResponseStatus,
headers: HTTPHeaders
) {
self.body = Body(bag)
self.version = version
self.status = status
self.headers = headers
}
}
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
extension HTTPClientResponse.Body: AsyncSequence {
typealias Element = ByteBuffer
typealias AsyncIterator = Iterator
struct Iterator: AsyncIteratorProtocol {
typealias Element = ByteBuffer
private let stream: IteratorStream
fileprivate init(stream: IteratorStream) {
self.stream = stream
}
func next() async throws -> ByteBuffer? {
try await self.stream.next()
}
}
func makeAsyncIterator() -> Iterator {
Iterator(stream: IteratorStream(bag: self.bag))
}
}
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
extension HTTPClientResponse.Body {
/// The purpose of this object is to inform the transaction about the response body being deinitialized.
/// If the users has not called `makeAsyncIterator` on the body, before it is deinited, the http
/// request needs to be cancelled.
fileprivate class ResponseRef {
private let transaction: Transaction
init(transaction: Transaction) {
self.transaction = transaction
}
deinit {
self.transaction.responseBodyDeinited()
}
}
}
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
extension HTTPClientResponse.Body {
internal class IteratorStream {
struct ID: Hashable {
private let objectID: ObjectIdentifier
init(_ object: IteratorStream) {
self.objectID = ObjectIdentifier(object)
}
}
private var id: ID { ID(self) }
private let bag: Transaction
init(bag: Transaction) {
self.bag = bag
}
deinit {
self.bag.responseBodyIteratorDeinited(streamID: self.id)
}
func next() async throws -> ByteBuffer? {
try await self.bag.nextResponsePart(streamID: self.id)
}
}
}
#endif