1515import Logging
1616import NIOCore
1717import NIOHTTP1
18+ import ServiceContextModule
19+ import Tracing
1820
1921import struct Foundation. URL
2022
@@ -30,18 +32,20 @@ extension HTTPClient {
3032 public func execute(
3133 _ request: HTTPClientRequest ,
3234 deadline: NIODeadline ,
33- logger: Logger ? = nil
35+ logger: Logger ? = nil ,
36+ context: ServiceContext ? = nil
3437 ) async throws -> HTTPClientResponse {
3538 try await self . executeAndFollowRedirectsIfNeeded (
3639 request,
3740 deadline: deadline,
3841 logger: logger ?? Self . loggingDisabled,
42+ context: context ?? . current ?? . topLevel,
3943 redirectState: RedirectState ( self . configuration. redirectConfiguration. mode, initialURL: request. url)
4044 )
4145 }
4246}
4347
44- // MARK: Connivence methods
48+ // MARK: Convenience methods
4549
4650@available ( macOS 10 . 15 , iOS 13 . 0 , watchOS 6 . 0 , tvOS 13 . 0 , * )
4751extension HTTPClient {
@@ -55,12 +59,14 @@ extension HTTPClient {
5559 public func execute(
5660 _ request: HTTPClientRequest ,
5761 timeout: TimeAmount ,
58- logger: Logger ? = nil
62+ logger: Logger ? = nil ,
63+ context: ServiceContext ? = nil
5964 ) async throws -> HTTPClientResponse {
6065 try await self . execute (
6166 request,
6267 deadline: . now( ) + timeout,
63- logger: logger
68+ logger: logger,
69+ context: context
6470 )
6571 }
6672}
@@ -71,15 +77,23 @@ extension HTTPClient {
7177 _ request: HTTPClientRequest ,
7278 deadline: NIODeadline ,
7379 logger: Logger ,
80+ context: ServiceContext ,
7481 redirectState: RedirectState ?
7582 ) async throws -> HTTPClientResponse {
7683 var currentRequest = request
7784 var currentRedirectState = redirectState
85+ var resendCount = 0
7886
7987 // this loop is there to follow potential redirects
8088 while true {
8189 let preparedRequest = try HTTPClientRequest . Prepared ( currentRequest, dnsOverride: configuration. dnsOverride)
82- let response = try await self . executeCancellable ( preparedRequest, deadline: deadline, logger: logger)
90+ let response = try await self . executeCancellable (
91+ preparedRequest,
92+ deadline: deadline,
93+ logger: logger,
94+ context: context,
95+ resendCount: resendCount > 0 ? resendCount : nil
96+ )
8397
8498 guard var redirectState = currentRedirectState else {
8599 // a `nil` redirectState means we should not follow redirects
@@ -112,46 +126,76 @@ extension HTTPClient {
112126 return response
113127 }
114128
129+ resendCount += 1
115130 currentRequest = newRequest
116131 }
117132 }
118133
119134 private func executeCancellable(
120135 _ request: HTTPClientRequest . Prepared ,
121136 deadline: NIODeadline ,
122- logger: Logger
137+ logger: Logger ,
138+ context: ServiceContext ,
139+ resendCount: Int ?
123140 ) async throws -> HTTPClientResponse {
124141 let cancelHandler = TransactionCancelHandler ( )
125142
126- return try await withTaskCancellationHandler (
127- operation: { ( ) async throws -> HTTPClientResponse in
128- let eventLoop = self . eventLoopGroup. any ( )
129- let deadlineTask = eventLoop. scheduleTask ( deadline: deadline) {
130- cancelHandler. cancel ( reason: . deadlineExceeded)
131- }
132- defer {
133- deadlineTask. cancel ( )
134- }
135- return try await withCheckedThrowingContinuation {
136- ( continuation: CheckedContinuation < HTTPClientResponse , Swift . Error > ) -> Void in
137- let transaction = Transaction (
138- request: request,
139- requestOptions: . fromClientConfiguration( self . configuration) ,
140- logger: logger,
141- connectionDeadline: . now( ) + ( self . configuration. timeout. connectionCreationTimeout) ,
142- preferredEventLoop: eventLoop,
143- responseContinuation: continuation
144- )
145-
146- cancelHandler. registerTransaction ( transaction)
147-
148- self . poolManager. executeRequest ( transaction)
149- }
150- } ,
151- onCancel: {
152- cancelHandler. cancel ( reason: . taskCanceled)
143+ return try await withSpan ( request. head. method. rawValue, context: context, ofKind: . client) { span in
144+ var request = request
145+ request. head. headers. propagate ( span. context)
146+ span. updateAttributes { attributes in
147+ attributes [ " http.request.method " ] = request. head. method. rawValue
148+ attributes [ " server.address " ] = request. poolKey. connectionTarget. host
149+ attributes [ " server.port " ] = request. poolKey. connectionTarget. port
150+ attributes [ " url.full " ] = request. url. absoluteString
151+ attributes [ " http.request.resend_count " ] = resendCount
153152 }
154- )
153+
154+ do {
155+ let response = try await withTaskCancellationHandler (
156+ operation: { ( ) async throws -> HTTPClientResponse in
157+ let eventLoop = self . eventLoopGroup. any ( )
158+ let deadlineTask = eventLoop. scheduleTask ( deadline: deadline) {
159+ cancelHandler. cancel ( reason: . deadlineExceeded)
160+ }
161+ defer {
162+ deadlineTask. cancel ( )
163+ }
164+ let response = try await withCheckedThrowingContinuation {
165+ ( continuation: CheckedContinuation < HTTPClientResponse , Swift . Error > ) -> Void in
166+ let transaction = Transaction (
167+ request: request,
168+ requestOptions: . fromClientConfiguration( self . configuration) ,
169+ logger: logger,
170+ connectionDeadline: . now( ) + ( self . configuration. timeout. connectionCreationTimeout) ,
171+ preferredEventLoop: eventLoop,
172+ responseContinuation: continuation
173+ )
174+
175+ cancelHandler. registerTransaction ( transaction)
176+
177+ self . poolManager. executeRequest ( transaction)
178+ }
179+ if response. status. code >= 400 {
180+ span. setStatus ( . init( code: . error) )
181+ span. attributes [ " error.type " ] = " \( response. status. code) "
182+ }
183+ span. attributes [ " http.response.status_code " ] = " \( response. status. code) "
184+ return response
185+ } ,
186+ onCancel: {
187+ span. setStatus ( . init( code: . error) )
188+ span. attributes [ " error.type " ] = " \( CancellationError . self) "
189+ cancelHandler. cancel ( reason: . taskCanceled)
190+ }
191+ )
192+ return response
193+ } catch {
194+ span. setStatus ( . init( code: . error) )
195+ span. attributes [ " error.type " ] = " \( type ( of: error) ) "
196+ throw error
197+ }
198+ }
155199 }
156200}
157201
0 commit comments