@@ -205,28 +205,42 @@ public class HTTPClient {
205
205
switch eventLoop. preference {
206
206
case . indifferent:
207
207
return self . execute ( request: request, delegate: delegate, eventLoop: self . eventLoopGroup. next ( ) , deadline: deadline)
208
- case . prefers( let preferred) :
209
- precondition ( self . eventLoopGroup. makeIterator ( ) . contains { $0 === preferred } , " Provided EventLoop must be part of clients EventLoopGroup. " )
210
- return self . execute ( request: request, delegate: delegate, eventLoop: preferred, deadline: deadline)
208
+ case . delegate( on: let eventLoop) :
209
+ precondition ( self . eventLoopGroup. makeIterator ( ) . contains { $0 === eventLoop } , " Provided EventLoop must be part of clients EventLoopGroup. " )
210
+ return self . execute ( request: request, delegate: delegate, eventLoop: eventLoop, deadline: deadline)
211
+ case . delegateAndChannel( on: let eventLoop) :
212
+ precondition ( self . eventLoopGroup. makeIterator ( ) . contains { $0 === eventLoop } , " Provided EventLoop must be part of clients EventLoopGroup. " )
213
+ return self . execute ( request: request, delegate: delegate, eventLoop: eventLoop, deadline: deadline)
214
+ case . testOnly_exact( channelOn: let channelEL, delegateOn: let delegateEL) :
215
+ return self . execute ( request: request,
216
+ delegate: delegate,
217
+ eventLoop: delegateEL,
218
+ channelEL: channelEL,
219
+ deadline: deadline)
211
220
}
212
221
}
213
222
214
223
private func execute< Delegate: HTTPClientResponseDelegate > ( request: Request ,
215
224
delegate: Delegate ,
216
- eventLoop: EventLoop ,
225
+ eventLoop delegateEL: EventLoop ,
226
+ channelEL: EventLoop ? = nil ,
217
227
deadline: NIODeadline ? = nil ) -> Task < Delegate . Response > {
218
228
let redirectHandler : RedirectHandler < Delegate . Response > ?
219
229
if self . configuration. followRedirects {
220
230
redirectHandler = RedirectHandler < Delegate . Response > ( request: request) { newRequest in
221
- self . execute ( request: newRequest, delegate: delegate, eventLoop: eventLoop, deadline: deadline)
231
+ self . execute ( request: newRequest,
232
+ delegate: delegate,
233
+ eventLoop: delegateEL,
234
+ channelEL: channelEL,
235
+ deadline: deadline)
222
236
}
223
237
} else {
224
238
redirectHandler = nil
225
239
}
226
240
227
- let task = Task < Delegate . Response > ( eventLoop: eventLoop )
241
+ let task = Task < Delegate . Response > ( eventLoop: delegateEL )
228
242
229
- var bootstrap = ClientBootstrap ( group: eventLoop )
243
+ var bootstrap = ClientBootstrap ( group: channelEL ?? delegateEL )
230
244
. channelOption ( ChannelOptions . socket ( SocketOptionLevel ( IPPROTO_TCP) , TCP_NODELAY) , value: 1 )
231
245
. channelInitializer { channel in
232
246
let encoder = HTTPRequestEncoder ( )
@@ -262,9 +276,7 @@ public class HTTPClient {
262
276
. flatMap { channel in
263
277
channel. writeAndFlush ( request)
264
278
}
265
- . whenFailure { error in
266
- task. fail ( error)
267
- }
279
+ . cascadeFailure ( to: task. promise)
268
280
269
281
return task
270
282
}
@@ -351,8 +363,12 @@ public class HTTPClient {
351
363
enum Preference {
352
364
/// Event Loop will be selected by the library.
353
365
case indifferent
354
- /// Library will try to use provided event loop if possible.
355
- case prefers( EventLoop )
366
+ /// The delegate will be run on the specified EventLoop (and the Channel if possible).
367
+ case delegate( on: EventLoop )
368
+ /// The delegate and the `Channel` will be run on the specified EventLoop.
369
+ case delegateAndChannel( on: EventLoop )
370
+
371
+ case testOnly_exact( channelOn: EventLoop , delegateOn: EventLoop )
356
372
}
357
373
358
374
var preference : Preference
@@ -363,9 +379,28 @@ public class HTTPClient {
363
379
364
380
/// Event Loop will be selected by the library.
365
381
public static let indifferent = EventLoopPreference ( . indifferent)
382
+
366
383
/// Library will try to use provided event loop if possible.
384
+ @available ( * , deprecated, renamed: " delegate(on:) " )
367
385
public static func prefers( _ eventLoop: EventLoop ) -> EventLoopPreference {
368
- return EventLoopPreference ( . prefers( eventLoop) )
386
+ return EventLoopPreference ( . delegate( on: eventLoop) )
387
+ }
388
+
389
+ /// The delegate will be run on the specified EventLoop (and the Channel if possible).
390
+ ///
391
+ /// This will call the configured delegate on `eventLoop` and will try to use a `Channel` on the same
392
+ /// `EventLoop` but will not establish a new network connection just to satisfy the `EventLoop` preference if
393
+ /// another existing connection on a different `EventLoop` is readily available from a connection pool.
394
+ public static func delegate( on eventLoop: EventLoop ) -> EventLoopPreference {
395
+ return EventLoopPreference ( . delegate( on: eventLoop) )
396
+ }
397
+
398
+ /// The delegate and the `Channel` will be run on the specified EventLoop.
399
+ ///
400
+ /// Use this for use-cases where you prefer a new connection to be established over re-using an existing
401
+ /// connection that might be on a different `EventLoop`.
402
+ public static func delegateAndChannel( on eventLoop: EventLoop ) -> EventLoopPreference {
403
+ return EventLoopPreference ( . delegateAndChannel( on: eventLoop) )
369
404
}
370
405
}
371
406
}
0 commit comments