1515import Logging
1616import NIOCore
1717import NIOHTTP1
18+ import ServiceContextModule
19+ import Tracing
1820
1921import struct Foundation. URL
2022
@@ -34,18 +36,20 @@ extension HTTPClient {
3436 public func execute(
3537 _ request: HTTPClientRequest ,
3638 deadline: NIODeadline ,
37- logger: Logger ? = nil
39+ logger: Logger ? = nil ,
40+ context: ServiceContext ? = nil
3841 ) async throws -> HTTPClientResponse {
3942 try await self . executeAndFollowRedirectsIfNeeded (
4043 request,
4144 deadline: deadline,
4245 logger: logger ?? Self . loggingDisabled,
46+ context: context ?? . current ?? . topLevel,
4347 redirectState: RedirectState ( self . configuration. redirectConfiguration. mode, initialURL: request. url)
4448 )
4549 }
4650}
4751
48- // MARK: Connivence methods
52+ // MARK: Convenience methods
4953
5054@available ( macOS 10 . 15 , iOS 13 . 0 , watchOS 6 . 0 , tvOS 13 . 0 , * )
5155extension HTTPClient {
@@ -63,12 +67,14 @@ extension HTTPClient {
6367 public func execute(
6468 _ request: HTTPClientRequest ,
6569 timeout: TimeAmount ,
66- logger: Logger ? = nil
70+ logger: Logger ? = nil ,
71+ context: ServiceContext ? = nil
6772 ) async throws -> HTTPClientResponse {
6873 try await self . execute (
6974 request,
7075 deadline: . now( ) + timeout,
71- logger: logger
76+ logger: logger,
77+ context: context
7278 )
7379 }
7480}
@@ -81,6 +87,7 @@ extension HTTPClient {
8187 _ request: HTTPClientRequest ,
8288 deadline: NIODeadline ,
8389 logger: Logger ,
90+ context: ServiceContext ,
8491 redirectState: RedirectState ?
8592 ) async throws -> HTTPClientResponse {
8693 var currentRequest = request
@@ -140,6 +147,7 @@ extension HTTPClient {
140147 return response
141148 }
142149
150+ resendCount += 1
143151 currentRequest = newRequest
144152 }
145153 }
@@ -149,39 +157,68 @@ extension HTTPClient {
149157 private func executeCancellable(
150158 _ request: HTTPClientRequest . Prepared ,
151159 deadline: NIODeadline ,
152- logger: Logger
160+ logger: Logger ,
161+ context: ServiceContext ,
162+ resendCount: Int ?
153163 ) async throws -> HTTPClientResponse {
154164 let cancelHandler = TransactionCancelHandler ( )
155165
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+ )
175197
176- cancelHandler. registerTransaction ( transaction)
198+ cancelHandler. registerTransaction ( transaction)
177199
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
183220 }
184- )
221+ }
185222 }
186223}
187224
0 commit comments