107 : mUrl(
std::move(
url)), mName(mUrl.substr(0, mUrl.find(
'?'))), mOptions(options)
110 mShared = std::make_unique<Shared>();
111 auto*
h = mShared->handle(mUrl, mOptions);
112 char error[CURL_ERROR_SIZE] = {};
113 curl_easy_setopt(
h, CURLOPT_ERRORBUFFER,
error);
114 curl_easy_setopt(
h, CURLOPT_NOBODY, 1L);
115 curl_easy_setopt(
h, CURLOPT_WRITEFUNCTION, discard);
116 auto code = curl_easy_perform(
h);
119 curl_easy_getinfo(
h, CURLINFO_RESPONSE_CODE, &status);
120 curl_easy_getinfo(
h, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &
length);
121 curl_easy_cleanup(
h);
122 if (code != CURLE_OK) {
129 throw IoError(mName,
"the server does not tell the size of the file");
151 std::vector<Transfer> transfers;
153 for (
auto const&
r : requests) {
154 checkRange(
r.offset,
r.length);
156 for (
size_t done = 0;
done <
r.length;
done += kMaxRequest) {
157 transfers.push_back({
r.offset +
done, std::min(kMaxRequest,
r.length -
done), (uint8_t*)
r.dst +
done});
160 if (transfers.empty()) {
164 auto* multi = curl_multi_init();
165 curl_multi_setopt(multi, CURLMOPT_PIPELINING, (
long)CURLPIPE_MULTIPLEX);
166 curl_multi_setopt(multi, CURLMOPT_MAX_HOST_CONNECTIONS, (
long)std::max(1, mOptions.
httpConnections));
167 std::vector<std::string> ranges;
168 ranges.reserve(transfers.size());
169 for (
auto& t : transfers) {
170 t.handle = mShared->handle(mUrl, mOptions);
172 curl_easy_setopt(t.handle, CURLOPT_RANGE, ranges.back().c_str());
173 curl_easy_setopt(t.handle, CURLOPT_WRITEFUNCTION, writeInto);
174 curl_easy_setopt(t.handle, CURLOPT_WRITEDATA, &t);
175 curl_easy_setopt(t.handle, CURLOPT_ERRORBUFFER, t.error);
176 curl_easy_setopt(t.handle, CURLOPT_PRIVATE, &t);
178 curl_easy_setopt(t.handle, CURLOPT_PIPEWAIT, 1L);
179 curl_multi_add_handle(multi, t.handle);
185 auto mc = curl_multi_perform(multi, &running);
186 if (mc != CURLM_OK) {
187 error = curl_multi_strerror(mc);
191 while (
auto*
msg = curl_multi_info_read(multi, &queued)) {
192 if (
msg->msg != CURLMSG_DONE) {
195 Transfer* t =
nullptr;
196 curl_easy_getinfo(
msg->easy_handle, CURLINFO_PRIVATE, (
char**)&t);
198 curl_easy_getinfo(
msg->easy_handle, CURLINFO_RESPONSE_CODE, &status);
199 if (!
error.empty()) {
204 }
else if (
msg->data.result != CURLE_OK) {
205 error = t->error[0] ? t->error : curl_easy_strerror(
msg->data.result);
206 }
else if (status != 206 && !(status == 200 && t->offset == 0 && t->length == mSize)) {
208 }
else if (t->received != t->length) {
213 curl_multi_poll(multi,
nullptr, 0, 1000,
nullptr);
215 }
while (running > 0 &&
error.empty());
217 for (
auto& t : transfers) {
218 curl_multi_remove_handle(multi, t.handle);
219 curl_easy_cleanup(t.handle);
221 curl_multi_cleanup(multi);
222 if (!
error.empty()) {
225 mReadCalls.fetch_add(transfers.size(), std::memory_order_relaxed);
226 mBytesRead.fetch_add(total, std::memory_order_relaxed);