15
15
import Logging
16
16
import NIOCore
17
17
import NIOHTTP1
18
+ import ServiceContextModule
19
+ import Tracing
18
20
19
21
import struct Foundation. URL
20
22
@@ -34,18 +36,20 @@ extension HTTPClient {
34
36
public func execute(
35
37
_ request: HTTPClientRequest ,
36
38
deadline: NIODeadline ,
37
- logger: Logger ? = nil
39
+ logger: Logger ? = nil ,
40
+ context: ServiceContext ? = nil
38
41
) async throws -> HTTPClientResponse {
39
42
try await self . executeAndFollowRedirectsIfNeeded (
40
43
request,
41
44
deadline: deadline,
42
45
logger: logger ?? Self . loggingDisabled,
46
+ context: context ?? . current ?? . topLevel,
43
47
redirectState: RedirectState ( self . configuration. redirectConfiguration. mode, initialURL: request. url)
44
48
)
45
49
}
46
50
}
47
51
48
- // MARK: Connivence methods
52
+ // MARK: Convenience methods
49
53
50
54
@available ( macOS 10 . 15 , iOS 13 . 0 , watchOS 6 . 0 , tvOS 13 . 0 , * )
51
55
extension HTTPClient {
@@ -63,12 +67,14 @@ extension HTTPClient {
63
67
public func execute(
64
68
_ request: HTTPClientRequest ,
65
69
timeout: TimeAmount ,
66
- logger: Logger ? = nil
70
+ logger: Logger ? = nil ,
71
+ context: ServiceContext ? = nil
67
72
) async throws -> HTTPClientResponse {
68
73
try await self . execute (
69
74
request,
70
75
deadline: . now( ) + timeout,
71
- logger: logger
76
+ logger: logger,
77
+ context: context
72
78
)
73
79
}
74
80
}
@@ -81,6 +87,7 @@ extension HTTPClient {
81
87
_ request: HTTPClientRequest ,
82
88
deadline: NIODeadline ,
83
89
logger: Logger ,
90
+ context: ServiceContext ,
84
91
redirectState: RedirectState ?
85
92
) async throws -> HTTPClientResponse {
86
93
var currentRequest = request
@@ -140,6 +147,7 @@ extension HTTPClient {
140
147
return response
141
148
}
142
149
150
+ resendCount += 1
143
151
currentRequest = newRequest
144
152
}
145
153
}
@@ -149,39 +157,68 @@ extension HTTPClient {
149
157
private func executeCancellable(
150
158
_ request: HTTPClientRequest . Prepared ,
151
159
deadline: NIODeadline ,
152
- logger: Logger
160
+ logger: Logger ,
161
+ context: ServiceContext ,
162
+ resendCount: Int ?
153
163
) async throws -> HTTPClientResponse {
154
164
let cancelHandler = TransactionCancelHandler ( )
155
165
156
- return try await withTaskCancellationHandler (
157
- operation: { ( ) async throws -> HTTPClientResponse in
158
- let eventLoop = self . eventLoopGroup. any ( )
159
- let deadlineTask = eventLoop. scheduleTask ( deadline: deadline) {
160
- cancelHandler. cancel ( reason: . deadlineExceeded)
161
- }
162
- defer {
163
- deadlineTask. cancel ( )
164
- }
165
- return try await withCheckedThrowingContinuation {
166
- ( continuation: CheckedContinuation < HTTPClientResponse , Swift . Error > ) -> Void in
167
- let transaction = Transaction (
168
- request: request,
169
- requestOptions: . fromClientConfiguration( self . configuration) ,
170
- logger: logger,
171
- connectionDeadline: . now( ) + ( self . configuration. timeout. connectionCreationTimeout) ,
172
- preferredEventLoop: eventLoop,
173
- responseContinuation: continuation
174
- )
166
+ return try await withSpan ( request. head. method. rawValue, context: context, ofKind: . client) { span in
167
+ var request = request
168
+ request. head. headers. propagate ( span. context)
169
+ span. updateAttributes { attributes in
170
+ attributes [ " http.request.method " ] = request. head. method. rawValue
171
+ attributes [ " server.address " ] = request. poolKey. connectionTarget. host
172
+ attributes [ " server.port " ] = request. poolKey. connectionTarget. port
173
+ attributes [ " url.full " ] = request. url. absoluteString
174
+ attributes [ " http.request.resend_count " ] = resendCount
175
+ }
176
+
177
+ do {
178
+ let response = try await withTaskCancellationHandler (
179
+ operation: { ( ) async throws -> HTTPClientResponse in
180
+ let eventLoop = self . eventLoopGroup. any ( )
181
+ let deadlineTask = eventLoop. scheduleTask ( deadline: deadline) {
182
+ cancelHandler. cancel ( reason: . deadlineExceeded)
183
+ }
184
+ defer {
185
+ deadlineTask. cancel ( )
186
+ }
187
+ let response = try await withCheckedThrowingContinuation {
188
+ ( continuation: CheckedContinuation < HTTPClientResponse , Swift . Error > ) -> Void in
189
+ let transaction = Transaction (
190
+ request: request,
191
+ requestOptions: . fromClientConfiguration( self . configuration) ,
192
+ logger: logger,
193
+ connectionDeadline: . now( ) + ( self . configuration. timeout. connectionCreationTimeout) ,
194
+ preferredEventLoop: eventLoop,
195
+ responseContinuation: continuation
196
+ )
175
197
176
- cancelHandler. registerTransaction ( transaction)
198
+ cancelHandler. registerTransaction ( transaction)
177
199
178
- self . poolManager. executeRequest ( transaction)
179
- }
180
- } ,
181
- onCancel: {
182
- cancelHandler. cancel ( reason: . taskCanceled)
200
+ self . poolManager. executeRequest ( transaction)
201
+ }
202
+ if response. status. code >= 400 {
203
+ span. setStatus ( . init( code: . error) )
204
+ span. attributes [ " error.type " ] = " \( response. status. code) "
205
+ }
206
+ span. attributes [ " http.response.status_code " ] = " \( response. status. code) "
207
+ return response
208
+ } ,
209
+ onCancel: {
210
+ span. setStatus ( . init( code: . error) )
211
+ span. attributes [ " error.type " ] = " \( CancellationError . self) "
212
+ cancelHandler. cancel ( reason: . taskCanceled)
213
+ }
214
+ )
215
+ return response
216
+ } catch {
217
+ span. setStatus ( . init( code: . error) )
218
+ span. attributes [ " error.type " ] = " \( type ( of: error) ) "
219
+ throw error
183
220
}
184
- )
221
+ }
185
222
}
186
223
}
187
224
0 commit comments