38 using T_AsyncLoopWakeUp =
typename T_AsyncEventLoop::LoopWakeUp;
39 using T_AsyncBufferView =
typename T_AsyncStreams::BufferView;
40 using T_AsyncBuffersPool =
typename T_AsyncStreams::BuffersPool;
41 using T_AsyncReadableStream =
typename T_AsyncStreams::ReadableStream;
42 using T_AsyncWritableStream =
typename T_AsyncStreams::WritableStream;
43 using T_AsyncReadableRequest =
typename T_AsyncReadableStream::Request;
44 using T_AsyncWritableRequest =
typename T_AsyncWritableStream::Request;
46 static constexpr int MaxListeners = 8;
50 virtual Result asyncRead()
override {
return Result(
true); }
55 Result init(T_AsyncBuffersPool& buffersPool, Span<T_AsyncWritableRequest> writeQueueSpan,
59 this->setWriteQueue(writeQueueSpan);
60 return T_AsyncWritableStream::init(buffersPool);
64 virtual Result asyncWrite(
typename T_AsyncBufferView::ID bufferID,
65 Function<
void(
typename T_AsyncBufferView::ID)> cb)
override
68 return Result::Error(HttpClientResultCategory, HttpClientError::AsyncRequestBodySinkOwnerMissing);
69 owner->onRequestBodyWritableBuffer(bufferID, move(cb));
73 virtual Result asyncDestroyWritable()
override
75 if (owner !=
nullptr and owner->operation.isRequestInFlight() and not owner->requestBodyFinished)
77 owner->onRequestBodyWritableError(
78 Result::Error(HttpClientResultCategory, HttpClientError::AsyncRequestBodySinkDestroyed));
80 T_AsyncWritableStream::finishedDestroyingWritable();
84 virtual bool canEndWritable()
override
88 owner->onRequestBodyWritableFinished();
96 HttpClientEvent<MaxListeners, HttpClientResponse&> eventResponseHead;
102 return Result::Error(HttpClientResultCategory, HttpClientError::AsyncAlreadyInitialized);
103 if (asyncMemory.responseBuffers.empty())
104 return Result::Error(HttpClientResultCategory, HttpClientError::AsyncResponseBuffersMissing);
105 if (asyncMemory.responseReadQueue.empty())
106 return Result::Error(HttpClientResultCategory, HttpClientError::AsyncResponseReadQueueMissing);
107 if (asyncMemory.requestWriteQueue.empty())
108 return Result::Error(HttpClientResultCategory, HttpClientError::AsyncRequestWriteQueueMissing);
110 if (asyncMemory.responseBufferMemory.sizeInBytes() > 0)
112 SC_TRY(T_AsyncBuffersPool::sliceInEqualParts(asyncMemory.responseBuffers, asyncMemory.responseBufferMemory,
113 asyncMemory.responseBuffers.sizeInElements()));
116 SC_TRY(operation.init(client, operationMemory));
119 responseReadQueue = asyncMemory.responseReadQueue;
120 requestWriteQueue = asyncMemory.requestWriteQueue;
122 responseBuffersPool.setBuffers(asyncMemory.responseBuffers);
124 responseBodyStream.setReadQueue(responseReadQueue);
125 responseBodyStream.setAutoDestroy(
false);
126 requestBodySink.setAutoDestroy(
false);
127 wakeUp.callback = [
this](
typename T_AsyncLoopWakeUp::Result& result) { onWakeUp(result); };
141 if (wakeUpStarted and eventLoop !=
nullptr)
143 (void)wakeUp.stop(*eventLoop);
144 wakeUpStarted =
false;
147 if (requestBodySink.isStillWriting() or not requestBodySink.hasBeenDestroyed())
149 requestBodySink.destroy();
151 responseBodyStream.destroy();
152 resetRequestBodyState();
154 SC_TRY(operation.close());
158 requestBodyBuffersPool =
nullptr;
163 Result cancel() {
return operation.cancel(); }
165 Result start(
const HttpClientRequest& request, HttpClientResponse& response,
166 T_AsyncBuffersPool* requestBodyPool =
nullptr)
169 return Result::Error(HttpClientResultCategory, HttpClientError::AsyncNotInitialized);
170 if (request.body.isStreamed())
172 if (requestBodyPool ==
nullptr)
173 return Result::Error(HttpClientResultCategory, HttpClientError::AsyncRequestBodyPoolMissing);
176 requestBodyBuffersPool = requestBodyPool;
177 resetRequestBodyState();
179 responseBodyStream.setReadQueue(responseReadQueue);
180 SC_TRY(responseBodyStream.init(responseBuffersPool));
181 SC_TRY(responseBodyStream.start());
183 if (request.body.isStreamed())
185 SC_TRY(requestBodySink.init(*requestBodyBuffersPool, requestWriteQueue, *
this));
188 if (not wakeUpStarted)
190 SC_TRY(wakeUp.start(*eventLoop));
191 wakeUpStarted =
true;
194 HttpClientRequest asyncRequest = request;
195 if (asyncRequest.body.isStreamed())
197 asyncRequest.body.provider =
this;
200 const Result res = operation.
start(asyncRequest, response,
this);
203 if (request.body.isStreamed())
205 requestBodySink.destroy();
207 responseBodyStream.destroy();
213 [[nodiscard]] T_AsyncReadableStream& getResponseBodyStream() {
return responseBodyStream; }
214 [[nodiscard]] T_AsyncWritableStream& getRequestBodySink() {
return requestBodySink; }
216 [[nodiscard]]
bool isInitialized()
const {
return initialized; }
217 [[nodiscard]]
bool isRequestInFlight()
const {
return operation.isRequestInFlight(); }
220 friend struct HttpClientTest;
221 virtual void onResponseHead(HttpClientResponse& response)
override { eventResponseHead.emit(response); }
223 virtual void onResponseBody(Span<const char> data)
override
225 typename T_AsyncBufferView::ID bufferID;
227 const Result bufferResult = responseBuffersPool.requestNewBuffer(data.sizeInBytes(), bufferID, writable);
228 if (not bufferResult)
230 responseBodyStream.emitError(bufferResult);
231 (void)operation.cancel();
234 memcpy(writable.data(), data.data(), data.sizeInBytes());
235 const bool continuePushing = responseBodyStream.push(bufferID, data.sizeInBytes());
236 responseBuffersPool.unrefBuffer(bufferID);
237 if (not continuePushing)
239 (void)operation.cancel();
243 virtual void onResponseComplete()
override
245 responseBodyStream.pushEnd();
246 resetRequestBodyState();
249 virtual void onError(Result error)
override
251 responseBodyStream.emitError(error);
252 if (requestBodyBuffersPool !=
nullptr)
254 requestBodySink.destroy();
256 resetRequestBodyState();
259 virtual Result pullRequestBody(Span<char> dest,
size_t& bytesWritten,
bool& endReached)
override
264 requestBodyMutex.lock();
265 while (not requestBodyHasActive and not requestBodyFinished and not requestBodyErrorSet)
267 requestBodyCV.wait(requestBodyMutex);
270 if (requestBodyErrorSet)
272 const Result error = requestBodyError;
273 requestBodyMutex.unlock();
277 if (not requestBodyHasActive and requestBodyFinished)
279 requestBodyMutex.unlock();
284 const typename T_AsyncBufferView::ID activeBuffer = requestBodyActiveBuffer;
285 const size_t activeOffset = requestBodyActiveOffset;
286 requestBodyMutex.unlock();
288 if (requestBodyBuffersPool ==
nullptr)
289 return Result::Error(HttpClientResultCategory, HttpClientError::AsyncRequestBodyPoolMissing);
291 Span<const char> readable;
292 SC_TRY(requestBodyBuffersPool->getReadableData(activeBuffer, readable));
294 const size_t remaining = readable.sizeInBytes() - activeOffset;
295 bytesWritten = remaining < dest.sizeInBytes() ? remaining : dest.sizeInBytes();
296 if (bytesWritten > 0)
298 memcpy(dest.data(), readable.data() + activeOffset, bytesWritten);
301 requestBodyMutex.lock();
302 requestBodyActiveOffset += bytesWritten;
303 const bool consumedBuffer = requestBodyActiveOffset >= readable.sizeInBytes();
304 requestBodyMutex.unlock();
308 finishRequestBodyActive(Result(
true));
314 virtual void notifyHttpClientOperation(HttpClientOperation&)
override
316 if (wakeUpStarted and eventLoop !=
nullptr)
318 (void)wakeUp.wakeUp(*eventLoop);
322 void onWakeUp(
typename T_AsyncLoopWakeUp::Result& result)
324 const Result pollResult = operation.
poll();
327 responseBodyStream.emitError(pollResult);
329 result.reactivateRequest(
true);
332 void resetRequestBodyState()
334 requestBodyMutex.lock();
335 if (requestBodyHasActive and requestBodyBuffersPool !=
nullptr)
337 requestBodyBuffersPool->unrefBuffer(requestBodyActiveBuffer);
339 requestBodyActiveBuffer = {};
340 requestBodyActiveCallback = {};
341 requestBodyActiveOffset = 0;
342 requestBodyHasActive =
false;
343 requestBodyFinished =
false;
344 requestBodyErrorSet =
false;
345 requestBodyError = Result(
true);
346 requestBodyCV.broadcast();
347 requestBodyMutex.unlock();
350 void finishRequestBodyActive(Result res)
352 requestBodyMutex.lock();
353 if (requestBodyHasActive and requestBodyBuffersPool !=
nullptr)
355 typename T_AsyncBufferView::ID bufferID = requestBodyActiveBuffer;
357 Function<void(
typename T_AsyncBufferView::ID)> callback = move(requestBodyActiveCallback);
359 requestBodyHasActive =
false;
360 requestBodyActiveBuffer = {};
361 requestBodyActiveOffset = 0;
362 requestBodyBuffersPool->unrefBuffer(bufferID);
363 requestBodyMutex.unlock();
364 requestBodySink.finishedWriting(bufferID, move(callback), res);
368 requestBodyMutex.unlock();
372 void onRequestBodyWritableBuffer(
typename T_AsyncBufferView::ID bufferID,
373 Function<
void(
typename T_AsyncBufferView::ID)>&& cb)
375 requestBodyMutex.lock();
376 if (requestBodyBuffersPool !=
nullptr)
378 requestBodyBuffersPool->refBuffer(bufferID);
380 requestBodyActiveBuffer = bufferID;
381 requestBodyActiveCallback = move(cb);
382 requestBodyActiveOffset = 0;
383 requestBodyHasActive =
true;
384 requestBodyCV.broadcast();
385 requestBodyMutex.unlock();
388 void onRequestBodyWritableFinished()
390 requestBodyMutex.lock();
391 requestBodyFinished =
true;
392 requestBodyCV.broadcast();
393 requestBodyMutex.unlock();
396 void onRequestBodyWritableError(Result error)
398 requestBodyMutex.lock();
399 requestBodyErrorSet =
true;
400 requestBodyError = error;
401 requestBodyCV.broadcast();
402 requestBodyMutex.unlock();
405 HttpClientOperation operation;
406 T_AsyncEventLoop* eventLoop =
nullptr;
408 ResponseBodyStream responseBodyStream;
409 RequestBodySink requestBodySink;
410 T_AsyncBuffersPool responseBuffersPool;
412 Span<T_AsyncReadableRequest> responseReadQueue;
413 Span<T_AsyncWritableRequest> requestWriteQueue;
415 T_AsyncBuffersPool* requestBodyBuffersPool =
nullptr;
417 T_AsyncLoopWakeUp wakeUp;
418 bool wakeUpStarted =
false;
420 HttpClientLocalMutex requestBodyMutex;
421 HttpClientLocalConditionVariable requestBodyCV;
423 typename T_AsyncBufferView::ID requestBodyActiveBuffer = {};
424 Function<void(
typename T_AsyncBufferView::ID)> requestBodyActiveCallback;
426 size_t requestBodyActiveOffset = 0;
427 bool requestBodyHasActive =
false;
428 bool requestBodyFinished =
false;
429 bool requestBodyErrorSet =
false;
430 Result requestBodyError = Result(
true);
432 bool initialized =
false;