@@ -32,32 +32,92 @@ extension HTTPClient {
3232 ///
3333 /// - parameters:
3434 /// - closure: function that will be called to write actual bytes to the channel.
35+ @available ( * , deprecated, message: " StreamWriter is deprecated, please use StreamWriter2 " )
3536 public init ( closure: @escaping ( IOData ) -> EventLoopFuture < Void > ) {
3637 self . closure = closure
3738 }
3839
40+ // This is needed so we don't have build warnings in the client itself
41+ init ( internalClosure: @escaping ( IOData ) -> EventLoopFuture < Void > ) {
42+ self . closure = internalClosure
43+ }
44+
3945 /// Write data to server.
4046 ///
4147 /// - parameters:
4248 /// - data: `IOData` to write.
49+ @available ( * , deprecated, message: " " )
4350 public func write( _ data: IOData ) -> EventLoopFuture < Void > {
4451 return self . closure ( data)
4552 }
4653 }
4754
55+ public struct StreamWriter2 {
56+ public let allocator : ByteBufferAllocator
57+ let onChunk : ( IOData ) -> EventLoopFuture < Void >
58+ let onComplete : EventLoopPromise < Void >
59+
60+ public init ( allocator: ByteBufferAllocator , onChunk: @escaping ( IOData ) -> EventLoopFuture < Void > , onComplete: EventLoopPromise < Void > ) {
61+ self . allocator = allocator
62+ self . onChunk = onChunk
63+ self . onComplete = onComplete
64+ }
65+
66+ public func write( _ buffer: ByteBuffer ) -> EventLoopFuture < Void > {
67+ self . onChunk ( . byteBuffer( buffer) )
68+ }
69+
70+ public func write( _ data: IOData ) -> EventLoopFuture < Void > {
71+ self . onChunk ( data)
72+ }
73+
74+ public func write( _ buffer: ByteBuffer , promise: EventLoopPromise < Void > ? ) {
75+ self . onChunk ( . byteBuffer( buffer) ) . cascade ( to: promise)
76+ }
77+
78+ public func write( _ data: IOData , promise: EventLoopPromise < Void > ? ) {
79+ self . onChunk ( data) . cascade ( to: promise)
80+ }
81+
82+ public func end( ) {
83+ self . onComplete. succeed ( ( ) )
84+ }
85+
86+ public func fail( _ error: Error ) {
87+ self . onComplete. fail ( error)
88+ }
89+ }
90+
4891 /// Body size. Request validation will be failed with `HTTPClientErrors.contentLengthMissing` if nil,
4992 /// unless `Trasfer-Encoding: chunked` header is set.
5093 public var length : Int ?
5194 /// Body chunk provider.
5295 public var stream : ( StreamWriter ) -> EventLoopFuture < Void >
96+ var stream2 : ( ( StreamWriter2 ) -> Void ) ?
97+
98+ @available ( * , deprecated, message: " " )
99+ init ( length: Int ? , stream: @escaping ( StreamWriter ) -> EventLoopFuture < Void > ) {
100+ self . length = length
101+ self . stream = stream
102+ self . stream2 = nil
103+ }
104+
105+ init ( length: Int ? , stream: @escaping ( StreamWriter2 ) -> Void ) {
106+ self . length = length
107+ self . stream = { _ in
108+ preconditionFailure ( " stream writer 2 was called " )
109+ }
110+ self . stream2 = stream
111+ }
53112
54113 /// Create and stream body using `ByteBuffer`.
55114 ///
56115 /// - parameters:
57116 /// - buffer: Body `ByteBuffer` representation.
58117 public static func byteBuffer( _ buffer: ByteBuffer ) -> Body {
59- return Body ( length: buffer. readableBytes) { writer in
60- writer. write ( . byteBuffer( buffer) )
118+ return Body ( length: buffer. readableBytes) { ( writer: StreamWriter2 ) in
119+ writer. write ( . byteBuffer( buffer) , promise: nil )
120+ writer. end ( )
61121 }
62122 }
63123
@@ -67,17 +127,30 @@ extension HTTPClient {
67127 /// - length: Body size. Request validation will be failed with `HTTPClientErrors.contentLengthMissing` if nil,
68128 /// unless `Transfer-Encoding: chunked` header is set.
69129 /// - stream: Body chunk provider.
130+ @available ( * , deprecated, message: " StreamWriter is deprecated, please use StreamWriter2 instead " )
70131 public static func stream( length: Int ? = nil , _ stream: @escaping ( StreamWriter ) -> EventLoopFuture < Void > ) -> Body {
71132 return Body ( length: length, stream: stream)
72133 }
73134
135+ /// Create and stream body using `StreamWriter`.
136+ ///
137+ /// - parameters:
138+ /// - length: Body size. Request validation will be failed with `HTTPClientErrors.contentLengthMissing` if nil,
139+ /// unless `Transfer-Encoding: chunked` header is set.
140+ /// - stream: Body chunk provider.
141+ public static func stream2( length: Int ? = nil , _ stream: @escaping ( StreamWriter2 ) -> Void ) -> Body {
142+ return Body ( length: length, stream: stream)
143+ }
144+
74145 /// Create and stream body using `Data`.
75146 ///
76147 /// - parameters:
77148 /// - data: Body `Data` representation.
78149 public static func data( _ data: Data ) -> Body {
79- return Body ( length: data. count) { writer in
80- writer. write ( . byteBuffer( ByteBuffer ( bytes: data) ) )
150+ return Body ( length: data. count) { ( writer: StreamWriter2 ) in
151+ let buffer = writer. allocator. buffer ( data: data)
152+ writer. write ( . byteBuffer( buffer) , promise: nil )
153+ writer. end ( )
81154 }
82155 }
83156
@@ -86,8 +159,10 @@ extension HTTPClient {
86159 /// - parameters:
87160 /// - string: Body `String` representation.
88161 public static func string( _ string: String ) -> Body {
89- return Body ( length: string. utf8. count) { writer in
90- writer. write ( . byteBuffer( ByteBuffer ( string: string) ) )
162+ return Body ( length: string. utf8. count) { ( writer: StreamWriter2 ) in
163+ let buffer = writer. allocator. buffer ( string: string)
164+ writer. write ( . byteBuffer( buffer) , promise: nil )
165+ writer. end ( )
91166 }
92167 }
93168 }
@@ -874,7 +949,32 @@ extension TaskHandler: ChannelDuplexHandler {
874949 let channel = context. channel
875950
876951 func doIt( ) -> EventLoopFuture < Void > {
877- return body. stream ( HTTPClient . Body. StreamWriter { part in
952+ if let stream2 = body. stream2 {
953+ let completion = channel. eventLoop. makePromise ( of: Void . self)
954+ stream2 ( HTTPClient . Body. StreamWriter2 ( allocator: channel. allocator, onChunk: { part in
955+ let promise = self . task. eventLoop. makePromise ( of: Void . self)
956+ // All writes have to be switched to the channel EL if channel and task ELs differ
957+ if channel. eventLoop. inEventLoop {
958+ self . writeBodyPart ( context: context, part: part, promise: promise)
959+ } else {
960+ channel. eventLoop. execute {
961+ self . writeBodyPart ( context: context, part: part, promise: promise)
962+ }
963+ }
964+
965+ promise. futureResult. whenFailure { error in
966+ completion. fail ( error)
967+ }
968+
969+ return promise. futureResult. map {
970+ self . callOutToDelegateFireAndForget ( value: part, self . delegate. didSendRequestPart)
971+ }
972+ } , onComplete: completion) )
973+
974+ return completion. futureResult
975+ }
976+
977+ return body. stream ( HTTPClient . Body. StreamWriter ( internalClosure: { part in
878978 let promise = self . task. eventLoop. makePromise ( of: Void . self)
879979 // All writes have to be switched to the channel EL if channel and task ELs differ
880980 if channel. eventLoop. inEventLoop {
@@ -888,7 +988,7 @@ extension TaskHandler: ChannelDuplexHandler {
888988 return promise. futureResult. map {
889989 self . callOutToDelegateFireAndForget ( value: part, self . delegate. didSendRequestPart)
890990 }
891- } )
991+ } ) )
892992 }
893993
894994 // Callout to the user to start body streaming should be on task EL
0 commit comments