Skip to content

Commit 24b0a14

Browse files
authored
Add async Transaction (#518)
This introduces an async Transaction object. The object is the translation layer between the user facing async API and the NIO channel handler.
1 parent c4feafd commit 24b0a14

File tree

8 files changed

+1925
-0
lines changed

8 files changed

+1925
-0
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the AsyncHTTPClient open source project
4+
//
5+
// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
#if compiler(>=5.5) && canImport(_Concurrency)
16+
import NIOCore
17+
import NIOHTTP1
18+
19+
@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)
20+
struct HTTPClientResponse {
21+
var version: HTTPVersion
22+
var status: HTTPResponseStatus
23+
var headers: HTTPHeaders
24+
var body: Body
25+
26+
struct Body {
27+
private let bag: Transaction
28+
private let reference: ResponseRef
29+
30+
fileprivate init(_ transaction: Transaction) {
31+
self.bag = transaction
32+
self.reference = ResponseRef(transaction: transaction)
33+
}
34+
}
35+
36+
init(
37+
bag: Transaction,
38+
version: HTTPVersion,
39+
status: HTTPResponseStatus,
40+
headers: HTTPHeaders
41+
) {
42+
self.body = Body(bag)
43+
self.version = version
44+
self.status = status
45+
self.headers = headers
46+
}
47+
}
48+
49+
@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)
50+
extension HTTPClientResponse.Body: AsyncSequence {
51+
typealias Element = ByteBuffer
52+
typealias AsyncIterator = Iterator
53+
54+
struct Iterator: AsyncIteratorProtocol {
55+
typealias Element = ByteBuffer
56+
57+
private let stream: IteratorStream
58+
59+
fileprivate init(stream: IteratorStream) {
60+
self.stream = stream
61+
}
62+
63+
func next() async throws -> ByteBuffer? {
64+
try await self.stream.next()
65+
}
66+
}
67+
68+
func makeAsyncIterator() -> Iterator {
69+
Iterator(stream: IteratorStream(bag: self.bag))
70+
}
71+
}
72+
73+
@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)
74+
extension HTTPClientResponse.Body {
75+
/// The purpose of this object is to inform the transaction about the response body being deinitialized.
76+
/// If the users has not called `makeAsyncIterator` on the body, before it is deinited, the http
77+
/// request needs to be cancelled.
78+
fileprivate class ResponseRef {
79+
private let transaction: Transaction
80+
81+
init(transaction: Transaction) {
82+
self.transaction = transaction
83+
}
84+
85+
deinit {
86+
self.transaction.responseBodyDeinited()
87+
}
88+
}
89+
}
90+
91+
@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)
92+
extension HTTPClientResponse.Body {
93+
internal class IteratorStream {
94+
struct ID: Hashable {
95+
private let objectID: ObjectIdentifier
96+
97+
init(_ object: IteratorStream) {
98+
self.objectID = ObjectIdentifier(object)
99+
}
100+
}
101+
102+
private var id: ID { ID(self) }
103+
private let bag: Transaction
104+
105+
init(bag: Transaction) {
106+
self.bag = bag
107+
}
108+
109+
deinit {
110+
self.bag.responseBodyIteratorDeinited(streamID: self.id)
111+
}
112+
113+
func next() async throws -> ByteBuffer? {
114+
try await self.bag.nextResponsePart(streamID: self.id)
115+
}
116+
}
117+
}
118+
119+
#endif

0 commit comments

Comments
 (0)