24namespace network_manager
29static const std::map<CURLcode, NetworkError>
errorsMap = {
73 const auto direction =
78 return stream->
Seek(offs, direction) ? CURL_SEEKFUNC_OK :
89 return stream->Seek(offs, origin) ? CURL_SEEKFUNC_OK : CURL_SEEKFUNC_FAIL;
97 mHandleManager (handleManager)
103 std::lock_guard<std::recursive_mutex> lock (
mStatusMutex);
109 std::lock_guard<std::recursive_mutex> lock (
mStatusMutex);
115 std::lock_guard<std::recursive_mutex> lock (
mStatusMutex);
121 std::lock_guard<std::recursive_mutex> lock (
mStatusMutex);
127 std::lock_guard<std::recursive_mutex> lock (
mStatusMutex);
133 std::lock_guard<std::mutex> lock (mHeadersMutex);
134 return mResponseHeaders.hasHeader (headerName);
167 std::lock_guard<std::recursive_mutex> lock (
mStatusMutex);
187 std::lock_guard<std::recursive_mutex> statusLock (
mStatusMutex);
213 if (buffer ==
nullptr || maxBytesCount == 0)
221 maxBytesCount = std::min<uint64_t> (maxBytesCount,
mDataBuffer.size ());
224 const auto end =
begin + maxBytesCount;
230 return maxBytesCount;
240 mForm = std::move(form);
248 handle.
setOption (CURLOPT_WRITEDATA,
this);
251 handle.
setOption (CURLOPT_HEADERDATA,
this);
254 handle.
setOption (CURLOPT_XFERINFODATA,
this);
259 handle.
setOption (CURLOPT_NOPROGRESS, 0L);
261 handle.
setOption (CURLOPT_CONNECTTIMEOUT_MS,
262 std::chrono::duration_cast<std::chrono::milliseconds> (
mRequest.
getTimeout()).count ()
267 curl_mime* mimeList =
nullptr;
269 if (
mForm !=
nullptr)
273 for (
size_t i = 0; i <
mForm->GetPartsCount(); ++i)
275 auto part =
mForm->GetPart(i);
277 curl_mimepart* curlPart = curl_mime_addpart(mimeList);
279 const auto& headers = part->GetHeaders();
281 if (headers.getHeadersCount() > 0)
283 curl_slist* partHeaders =
nullptr;
285 for (
auto header : headers)
287 partHeaders = curl_slist_append(
288 partHeaders, (header.Name +
": " + header.Value).c_str());
291 curl_mime_headers(curlPart, partHeaders, 1);
295 curlPart, part->GetSize(), curl_read_callback(
MimePartRead),
299 curl_easy_setopt(handle.
getCurlHandle(), CURLOPT_MIMEPOST, mimeList);
303 if (
const auto payloadSize =
mPayload->GetDataSize(); payloadSize > 0)
305 handle.
appendHeader({
"Transfer-Encoding", std::string() });
307 {
"Content-Length", std::to_string(payloadSize) });
310 handle.
setOption(CURLOPT_POSTFIELDSIZE_LARGE, payloadSize);
312 handle.
setOption(CURLOPT_INFILESIZE_LARGE, payloadSize);
323 handle.
setOption (CURLOPT_POSTFIELDS,
"");
324 handle.
setOption (CURLOPT_POSTFIELDSIZE, 0);
327 auto cleanupMime =
finally(
329 if (mimeList !=
nullptr)
330 curl_mime_free(mimeList);
336 const auto result = handle.
perform ();
340 std::lock_guard<std::recursive_mutex> lock (
mStatusMutex);
342 if (result.Code != CURLE_OK)
344 const auto it =
errorsMap.find (result.Code);
375 std::lock_guard<std::recursive_mutex> lock (request->mStatusMutex);
377 if (request->mAbortRequested)
380 if (!request->mHeadersReceived)
382 request->mHeadersReceived =
true;
385 assert (request->mCurrentHandle !=
nullptr);
387 if (request->mCurrentHandle !=
nullptr)
388 request->mHttpCode = request->mCurrentHandle->getHTTPCode ();
395 std::lock_guard<std::mutex> lock (request->mDataBufferMutex);
396 request->mDataBuffer.insert (request->mDataBuffer.end (), ptr, ptr +
size);
399 std::lock_guard<std::mutex> lock (request->mCallbackMutex);
401 if (request->mOnDataReceivedCallback)
402 request->mOnDataReceivedCallback (request);
410 std::lock_guard<std::recursive_mutex> lock (request->mStatusMutex);
412 if (request->mAbortRequested)
416 assert (request->mCurrentHandle !=
nullptr);
418 if (request->mCurrentHandle !=
nullptr)
419 request->mHttpCode = request->mCurrentHandle->getHTTPCode ();
429 std::lock_guard<std::mutex> lock (request->mHeadersMutex);
438 request->mCurrentHandle->markKeepAlive ();
440 request->mResponseHeaders.addHeader (header);
447 CurlResponse* clientp, curl_off_t dltotal, curl_off_t dlnow,
448 curl_off_t ultotal, curl_off_t ulnow)
noexcept
451 std::lock_guard<std::recursive_mutex> lock(clientp->mStatusMutex);
453 if (clientp->mAbortRequested)
457 std::lock_guard<std::mutex> callbackLock(clientp->mCallbackMutex);
459 if (dltotal > 0 && clientp->mDownloadProgressCallback)
460 clientp->mDownloadProgressCallback(dlnow, dltotal);
462 if (ultotal > 0 && clientp->mUploadProgressCallback)
463 clientp->mUploadProgressCallback(ulnow, ultotal);
Declare an implementation of IResponse using libcurl.
Declare a class for constructing HTTP requests.
CURL * getCurlHandle() const noexcept
CURLcode appendCookies(const CookiesList &cookie) noexcept
unsigned getHTTPCode() const noexcept
CURLcode setOption(CURLoption option, Args... value) noexcept
void appendHeader(const Header &header)
void appendHeaders(const HeadersList &headers)
Handle getHandle(RequestVerb verb, const std::string &url)
std::deque< uint8_t > mDataBuffer
virtual void setUploadProgressCallback(ProgressCallback callback) override
Set the upload progress callback.
void setForm(std::unique_ptr< MultipartData > form)
HeadersList mResponseHeaders
virtual void setDownloadProgressCallback(ProgressCallback callback) override
Set the download progress callback.
static size_t WriteCallback(const uint8_t *ptr, size_t size, size_t nmemb, CurlResponse *userdata) noexcept
uint64_t getBytesAvailable() const noexcept override
const CookiesList & getCookies() const noexcept override
std::unique_ptr< MultipartData > mForm
NetworkError getError() const noexcept override
void setPayload(RequestPayloadStreamPtr payload)
const Request & getRequest() const noexcept override
static int CurlProgressCallback(CurlResponse *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) noexcept
bool isFinished() const noexcept override
CurlHandleManager * mHandleManager
void setOnDataReceivedCallback(RequestCallback callback) override
std::mutex mDataBufferMutex
NetworkError mNetworkError
unsigned getHTTPCode() const noexcept override
CurlResponse(RequestVerb verb, const Request &request, CurlHandleManager *handleManager) noexcept
ProgressCallback mUploadProgressCallback
std::recursive_mutex mStatusMutex
std::mutex mCallbackMutex
std::string getErrorString() const override
bool hasHeader(const std::string &headerName) const noexcept override
std::string getHeader(const std::string &headerName) const override
const HeadersList & getHeaders() const noexcept override
static size_t HeaderCallback(const char *buffer, size_t size, size_t nitems, CurlResponse *userdata) noexcept
CurlHandleManager::Handle * mCurrentHandle
RequestCallback mRequestFinishedCallback
ProgressCallback mDownloadProgressCallback
void setRequestFinishedCallback(RequestCallback callback) override
void abort() noexcept override
uint64_t readData(void *buffer, uint64_t maxBytesCount) override
RequestCallback mOnDataReceivedCallback
CookiesList mResponseCookies
RequestPayloadStreamPtr mPayload
bool headersReceived() const noexcept override
std::string getURL() const override
std::function< void(int64_t current, int64_t expected)> ProgressCallback
std::function< void(IResponse *)> RequestCallback
virtual size_t Read(void *buffer, size_t maxBytes)=0
Timeout getTimeout() const noexcept
const HeadersList & getHeaders() const noexcept
const CookiesList & getCookies() noexcept
size_t getMaxRedirects() const noexcept
const std::string & getURL() const noexcept
virtual bool Seek(int64_t offset, SeekDirection direction)=0
returns true on success
virtual int64_t Read(void *buffer, int64_t size)=0
returns number of bytes read
size_t MimePartRead(char *ptr, size_t size, size_t nmemb, MultipartData::Part *stream)
int MimePartSeek(MultipartData::Part *stream, curl_off_t offs, int origin) noexcept
int DataStreamSeek(RequestPayloadStream *stream, curl_off_t offs, int origin)
size_t DataStreamRead(char *ptr, size_t size, size_t nmemb, RequestPayloadStream *stream)
static const std::map< CURLcode, NetworkError > errorsMap
std::shared_ptr< RequestPayloadStream > RequestPayloadStreamPtr
const char * end(const char *str) noexcept
const char * begin(const char *str) noexcept
void copy(const T *src, T *dst, int32_t n)
static Cookie Parse(const std::string &cookieString)