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
@@ -91,7 +98,13 @@ extension HTTPClient {
9198 while true {
9299 let preparedRequest = try HTTPClientRequest . Prepared ( currentRequest, dnsOverride: configuration. dnsOverride)
93100 let response = try await {
94- var response = try await self . executeCancellable ( preparedRequest, deadline: deadline, logger: logger)
101+ var response = try await self . executeCancellable (
102+ preparedRequest,
103+ deadline: deadline,
104+ logger: logger,
105+ context: context,
106+ resendCount: history. isEmpty ? nil : history. count
107+ )
95108
96109 history. append (
97110 . init(
@@ -149,39 +162,68 @@ extension HTTPClient {
149162 private func executeCancellable(
150163 _ request: HTTPClientRequest . Prepared ,
151164 deadline: NIODeadline ,
152- logger: Logger
165+ logger: Logger ,
166+ context: ServiceContext ,
167+ resendCount: Int ?
153168 ) async throws -> HTTPClientResponse {
154169 let cancelHandler = TransactionCancelHandler ( )
155170
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- )
171+ return try await withSpan ( request. head. method. rawValue, context: context, ofKind: . client) { span in
172+ var request = request
173+ request. head. headers. propagate ( span. context)
174+ span. updateAttributes { attributes in
175+ attributes [ " http.request.method " ] = request. head. method. rawValue
176+ attributes [ " server.address " ] = request. poolKey. connectionTarget. host
177+ attributes [ " server.port " ] = request. poolKey. connectionTarget. port
178+ attributes [ " url.full " ] = request. url. absoluteString
179+ attributes [ " http.request.resend_count " ] = resendCount
180+ }
181+
182+ do {
183+ let response = try await withTaskCancellationHandler (
184+ operation: { ( ) async throws -> HTTPClientResponse in
185+ let eventLoop = self . eventLoopGroup. any ( )
186+ let deadlineTask = eventLoop. scheduleTask ( deadline: deadline) {
187+ cancelHandler. cancel ( reason: . deadlineExceeded)
188+ }
189+ defer {
190+ deadlineTask. cancel ( )
191+ }
192+ let response = try await withCheckedThrowingContinuation {
193+ ( continuation: CheckedContinuation < HTTPClientResponse , Swift . Error > ) -> Void in
194+ let transaction = Transaction (
195+ request: request,
196+ requestOptions: . fromClientConfiguration( self . configuration) ,
197+ logger: logger,
198+ connectionDeadline: . now( ) + ( self . configuration. timeout. connectionCreationTimeout) ,
199+ preferredEventLoop: eventLoop,
200+ responseContinuation: continuation
201+ )
175202
176- cancelHandler. registerTransaction ( transaction)
203+ cancelHandler. registerTransaction ( transaction)
177204
178- self . poolManager. executeRequest ( transaction)
179- }
180- } ,
181- onCancel: {
182- cancelHandler. cancel ( reason: . taskCanceled)
205+ self . poolManager. executeRequest ( transaction)
206+ }
207+ if response. status. code >= 400 {
208+ span. setStatus ( . init( code: . error) )
209+ span. attributes [ " error.type " ] = " \( response. status. code) "
210+ }
211+ span. attributes [ " http.response.status_code " ] = " \( response. status. code) "
212+ return response
213+ } ,
214+ onCancel: {
215+ span. setStatus ( . init( code: . error) )
216+ span. attributes [ " error.type " ] = " \( CancellationError . self) "
217+ cancelHandler. cancel ( reason: . taskCanceled)
218+ }
219+ )
220+ return response
221+ } catch {
222+ span. setStatus ( . init( code: . error) )
223+ span. attributes [ " error.type " ] = " \( type ( of: error) ) "
224+ throw error
183225 }
184- )
226+ }
185227 }
186228}
187229
0 commit comments