Skip to content

Commit dd8ec55

Browse files
fabianfettglbrntt
andcommitted
Apply suggestions from code review
Co-authored-by: George Barnett <[email protected]>
1 parent 2a16ade commit dd8ec55

File tree

4 files changed

+57
-90
lines changed

4 files changed

+57
-90
lines changed

Diff for: Sources/AsyncHTTPClient/ConnectionPool/HTTPExecutingRequest.swift

+9-12
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ protocol HTTPScheduledRequest: AnyObject {
149149
/// A connection to run this task on needs to be found before this deadline!
150150
var connectionDeadline: NIODeadline { get }
151151

152-
/// The task's eventLoopPreference
152+
/// The task's `EventLoop` preference
153153
var eventLoopPreference: HTTPClient.EventLoopPreference { get }
154154

155155
/// Informs the task, that it was queued for execution
@@ -196,37 +196,34 @@ protocol HTTPExecutingRequest: AnyObject {
196196
/// after `requestHeadSent` was called.
197197
var requestHead: HTTPRequestHead { get }
198198

199-
/// The maximal `TimeAmount` that is allowed to pass between reads from the Channel.
199+
/// The maximal `TimeAmount` that is allowed to pass between `channelRead`s from the Channel.
200200
var idleReadTimeout: TimeAmount? { get }
201201

202-
/// Will be called by the ChannelHandler to indicate that the request is going to be send.
202+
/// Will be called by the ChannelHandler to indicate that the request is going to be sent.
203203
///
204204
/// This will be called on the Channel's EventLoop. Do **not block** during your execution!
205205
///
206-
/// - Returns: A bool indicating if the request should really be started. Return false if the request has already been cancelled.
207-
/// If the request is cancelled after this method call `executor.cancel()` to stop request execution.
206+
/// - Returns: A bool indicating if the request should be started. Return `false` if the request
207+
/// has already been cancelled. If the request is cancelled after the `willExecuteRequest`
208+
/// method was called. The executing request must call `executor.cancel()` to stop
209+
/// request execution.
208210
func willExecuteRequest(_: HTTPRequestExecutor) -> Bool
209211

210212
/// Will be called by the ChannelHandler to indicate that the request head has been sent.
211213
///
212214
/// This will be called on the Channel's EventLoop. Do **not block** during your execution!
213215
func requestHeadSent(_: HTTPRequestHead)
214216

215-
/// Start request streaming
217+
/// Start or resume request body streaming
216218
///
217219
/// This will be called on the Channel's EventLoop. Do **not block** during your execution!
218-
func startRequestBodyStream()
220+
func resumeRequestBodyStream()
219221

220222
/// Pause request streaming
221223
///
222224
/// This will be called on the Channel's EventLoop. Do **not block** during your execution!
223225
func pauseRequestBodyStream()
224226

225-
/// Resume request streaming
226-
///
227-
/// This will be called on the Channel's EventLoop. Do **not block** during your execution!
228-
func resumeRequestBodyStream()
229-
230227
/// Receive a response head.
231228
///
232229
/// Please note that `receiveResponseHead` and `receiveResponseBodyPart` may

Diff for: Sources/AsyncHTTPClient/RequestBag+StateMachine.swift

+30-58
Original file line numberDiff line numberDiff line change
@@ -92,39 +92,45 @@ extension RequestBag.StateMachine {
9292
}
9393
}
9494

95-
enum StartProducingAction {
96-
case startWriter(HTTPClient.Body.StreamWriter, body: HTTPClient.Body)
95+
enum ResumeProducingAction {
96+
case startWriter
97+
case succeedBackpressurePromise(EventLoopPromise<Void>?)
9798
case none
9899
}
99100

100-
mutating func startRequestBodyStream(
101-
_ body: HTTPClient.Body?,
102-
writeMethod: @escaping (IOData) -> EventLoopFuture<Void>
103-
) -> StartProducingAction {
101+
mutating func resumeRequestBodyStream() -> ResumeProducingAction {
104102
switch self.state {
103+
case .initialized, .queued:
104+
preconditionFailure("A request stream can only be resumed, if the request was started")
105+
105106
case .executing(let executor, .initialized, .initialized):
106-
guard let body = body else {
107-
preconditionFailure("Did not expect to get a call to `startRequestBodyStream`, if there is no body.")
108-
}
107+
self.state = .executing(executor, .producing, .initialized)
108+
return .startWriter
109109

110-
let streamWriter = HTTPClient.Body.StreamWriter { part -> EventLoopFuture<Void> in
111-
writeMethod(part)
112-
}
110+
case .executing(_, .producing, _):
111+
preconditionFailure("Expected that resume is only called when if we were paused before")
113112

114-
self.state = .executing(executor, .producing, .initialized)
113+
case .executing(let executor, .paused(let promise), let responseState):
114+
self.state = .executing(executor, .producing, responseState)
115+
return .succeedBackpressurePromise(promise)
115116

116-
return .startWriter(streamWriter, body: body)
117+
case .executing(_, .finished, _):
118+
// the channels writability changed to writable after we have forwarded all the
119+
// request bytes. Can be ignored.
120+
return .none
117121

118-
case .finished(error: .some):
122+
case .executing(_, .initialized, .buffering), .executing(_, .initialized, .waitingForRemote):
123+
preconditionFailure("Invalid states: Response can not be received before request")
124+
125+
case .redirected:
126+
// if we are redirected, we should cancel our request body stream anyway
119127
return .none
120128

121-
case .initialized,
122-
.queued,
123-
.executing(_, _, _),
124-
.finished(error: .none),
125-
.redirected,
126-
.modifying:
127-
preconditionFailure("Expected the state to be either initialized or failed. Invalid state: \(self.state)")
129+
case .finished:
130+
preconditionFailure("Invalid state")
131+
132+
case .modifying:
133+
preconditionFailure("Invalid state")
128134
}
129135
}
130136

@@ -156,37 +162,6 @@ extension RequestBag.StateMachine {
156162
}
157163
}
158164

159-
mutating func resumeRequestBodyStream() -> EventLoopPromise<Void>? {
160-
switch self.state {
161-
case .initialized, .queued:
162-
preconditionFailure("A request stream can only be resumed, if the request was started")
163-
case .executing(let executor, let requestState, let responseState):
164-
switch requestState {
165-
case .initialized:
166-
preconditionFailure("Request stream must be started before it can be paused")
167-
case .producing:
168-
preconditionFailure("Expected that resume is only called when if we were paused before")
169-
case .paused(let promise):
170-
self.state = .executing(executor, .producing, responseState)
171-
return promise
172-
case .finished:
173-
// the channels writability changed to writable after we have forwarded all the
174-
// request bytes. Can be ignored.
175-
return nil
176-
}
177-
178-
case .redirected:
179-
// if we are redirected, we should cancel our request body stream anyway
180-
return nil
181-
182-
case .finished:
183-
preconditionFailure("Invalid state")
184-
185-
case .modifying:
186-
preconditionFailure("Invalid state")
187-
}
188-
}
189-
190165
enum WriteAction {
191166
case write(IOData, HTTPRequestExecutor, EventLoopFuture<Void>)
192167

@@ -195,10 +170,6 @@ extension RequestBag.StateMachine {
195170
}
196171

197172
mutating func writeNextRequestPart(_ part: IOData, taskEventLoop: EventLoop) -> WriteAction {
198-
// this method is invoked with bodyPart and returns a future that signals that
199-
// more data can be send.
200-
// it may be invoked on any eventLoop
201-
202173
switch self.state {
203174
case .initialized, .queued:
204175
preconditionFailure("Invalid state: \(self.state)")
@@ -210,6 +181,8 @@ extension RequestBag.StateMachine {
210181
return .write(part, executor, taskEventLoop.makeSucceededFuture(()))
211182

212183
case .paused(.none):
184+
// backpressure is signaled to the writer using unfulfilled futures. if there
185+
// is no existing, unfulfilled promise, let's create a new one
213186
let promise = taskEventLoop.makePromise(of: Void.self)
214187
self.state = .executing(executor, .paused(promise), responseState)
215188
return .write(part, executor, promise.futureResult)
@@ -459,7 +432,6 @@ extension RequestBag.StateMachine {
459432
return .consume(byteBuffer)
460433
}
461434

462-
// buffer is empty, wait for more
463435
self.state = .finished(error: nil)
464436
return .finishStream
465437

Diff for: Sources/AsyncHTTPClient/RequestBag.swift

+17-19
Original file line numberDiff line numberDiff line change
@@ -115,32 +115,38 @@ final class RequestBag<Delegate: HTTPClientResponseDelegate>: HTTPScheduledReque
115115
}
116116
}
117117

118-
enum StartProducingAction {
119-
case startWriter(HTTPClient.Body.StreamWriter, body: HTTPClient.Body)
120-
case none
121-
}
122-
123-
func startRequestBodyStream() {
118+
func resumeRequestBodyStream() {
124119
let produceAction = self.stateLock.withLock {
125-
self._state.startRequestBodyStream(self.request.body, writeMethod: self.writeNextRequestPart)
120+
self._state.resumeRequestBodyStream()
126121
}
127122

128123
switch produceAction {
129-
case .startWriter(let writer, body: let body):
130-
func startWriter0(_ writer: HTTPClient.Body.StreamWriter, body: HTTPClient.Body) {
124+
case .startWriter:
125+
func startWriter0() {
126+
guard let body = self.request.body else {
127+
preconditionFailure("Expected to have a body, if the `HTTPRequestStateMachine` resume a request stream")
128+
}
129+
130+
let writer = HTTPClient.Body.StreamWriter {
131+
self.writeNextRequestPart($0)
132+
}
133+
131134
body.stream(writer).whenComplete {
132135
self.finishRequestBodyStream($0)
133136
}
134137
}
135138

136139
if self.task.eventLoop.inEventLoop {
137-
startWriter0(writer, body: body)
140+
startWriter0()
138141
} else {
139142
return self.task.eventLoop.execute {
140-
startWriter0(writer, body: body)
143+
startWriter0()
141144
}
142145
}
143146

147+
case .succeedBackpressurePromise(let promise):
148+
promise?.succeed(())
149+
144150
case .none:
145151
break
146152
}
@@ -152,14 +158,6 @@ final class RequestBag<Delegate: HTTPClientResponseDelegate>: HTTPScheduledReque
152158
}
153159
}
154160

155-
func resumeRequestBodyStream() {
156-
let promise = self.stateLock.withLock {
157-
self._state.resumeRequestBodyStream()
158-
}
159-
160-
promise?.succeed(())
161-
}
162-
163161
enum WriteAction {
164162
case write(IOData, HTTPRequestExecutor, EventLoopFuture<Void>)
165163

Diff for: Tests/AsyncHTTPClientTests/RequestBagTests.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ final class RequestBagTests: XCTestCase {
7474
bag.requestHeadSent(bag.requestHead)
7575
XCTAssertEqual(delegate.hitDidSendRequestHead, 1)
7676
streamIsAllowedToWrite = true
77-
bag.startRequestBodyStream()
77+
bag.resumeRequestBodyStream()
7878
streamIsAllowedToWrite = false
7979

8080
// after starting the body stream we should have received two writes

0 commit comments

Comments
 (0)