42 curl_easy_setopt(
m_curl.get(), CURLOPT_WRITEFUNCTION, CurlVectorReadOp::WriteCallback);
43 curl_easy_setopt(
m_curl.get(), CURLOPT_WRITEDATA,
this);
46 auto multiple =
false;
48 if (!chunk.GetLength())
continue;
49 if (multiple) {ss <<
",";}
50 ss << chunk.GetOffset() <<
"-" << chunk.GetOffset() + chunk.GetLength() - 1;
53 auto byte_range_val = ss.str();
54 if (byte_range_val.size()) {
63 std::string custom_msg = msg;
66 std::string offset =
"(unknown)";
67 std::string length =
"(unknown)";
72 if (!custom_msg.empty()) {
73 m_logger->
Debug(
kLogXrdClHttp,
"curl operation with vector starting offset %s / length %s failed with message: %s", offset.c_str(), length.c_str(), custom_msg.c_str());
74 custom_msg +=
" (vector read operation starting at offset " + offset +
" / length " + length +
")";
76 m_logger->
Debug(
kLogXrdClHttp,
"curl vector operation starting at offset %s / length %s failed with status code %d", offset.c_str(), length.c_str(), errNum);
109 if (
m_curl ==
nullptr)
return;
110 curl_easy_setopt(
m_curl.get(), CURLOPT_WRITEFUNCTION,
nullptr);
111 curl_easy_setopt(
m_curl.get(), CURLOPT_WRITEDATA,
nullptr);
112 curl_easy_setopt(
m_curl.get(), CURLOPT_HTTPHEADER,
nullptr);
113 curl_easy_setopt(
m_curl.get(), CURLOPT_OPENSOCKETFUNCTION,
nullptr);
114 curl_easy_setopt(
m_curl.get(), CURLOPT_OPENSOCKETDATA,
nullptr);
115 curl_easy_setopt(
m_curl.get(), CURLOPT_SOCKOPTFUNCTION,
nullptr);
116 curl_easy_setopt(
m_curl.get(), CURLOPT_SOCKOPTDATA,
nullptr);
138 m_current_op.second = std::numeric_limits<off_t>::max();
143 m_current_op.second = std::numeric_limits<off_t>::max();
146 auto buffer = orig_buffer;
147 auto length = orig_length;
169 auto to_copy = (
static_cast<size_t>(remaining) < length) ?
static_cast<size_t>(remaining) : length;
183 m_multipart_boundary =
true;
189 CalculateNextBuffer();
201 m_multipart_boundary =
true;
217 auto get_next_line = [&]() {
218 std::string_view chunk_header(buffer, length);
223 !chunk_header.empty() && chunk_header.front() ==
'\n') {
228 return std::make_pair(std::string_view(
m_header_line),
true);
230 auto pos = chunk_header.find(
"\r\n");
231 if (pos == std::string_view::npos) {
234 return std::make_pair(std::string_view(),
false);
236 auto line_part = chunk_header.substr(0, pos);
245 return std::make_pair(std::string_view(
m_header_line),
true);
250 bool last_segment =
false;
251 if (m_multipart_boundary) {
253 auto [line, ok] = get_next_line();
260 if (line.empty()) {
continue;}
268 std::stringstream ss;
269 ss <<
"Server has responded with an invalid boundary line: '" << line <<
"' (expected '" <<
m_headers.
MultipartSeparator() <<
"')";
279 auto [line, ok] = get_next_line();
281 m_multipart_boundary =
false;
287 auto header_name_end = line.find(
':');
288 if (header_name_end == std::string_view::npos) {
289 std::stringstream ss; ss <<
"Invalid header line in response from server: " << line;
292 auto header_name = line.substr(0, header_name_end);
295 if (header_name.size() != 13 || strncasecmp(header_name.data(),
"content-range", 13)) {
300 auto value = line.substr(header_name_end + 1);
303 while (!value.empty() && value[0] ==
' ') {
304 value = value.substr(1);
307 if (value.substr(0, 5) !=
"bytes") {
308 std::stringstream ss; ss <<
"Invalid Content-Range value (no 'bytes' unit): " << value;
312 value = value.substr(5);
313 while (!value.empty() && value[0] ==
' ') {
314 value = value.substr(1);
325 bytes_val = std::stoll(value.data(), &count);
326 }
catch (std::invalid_argument &) {
327 std::stringstream ss; ss <<
"Invalid Content-Range value (no integer in range start): " << value;
329 }
catch (std::out_of_range &) {
330 std::stringstream ss; ss <<
"Invalid Content-Range value (out of range): " << value;
333 if (value.size() <= count || value[count] !=
'-') {
334 std::stringstream ss; ss <<
"Invalid Content-Range value (no dash in range): " << value;
338 value = value.substr(count + 1);
340 bytes_val = std::stoll(value.data(), &count);
341 }
catch (std::invalid_argument &) {
342 std::stringstream ss; ss <<
"Invalid Content-Range value (no integer in range end): " << value;
344 }
catch (std::out_of_range &) {
345 std::stringstream ss; ss <<
"Invalid Content-Range value (out of range in range end): " << value;
348 if (value.size() <= count || value[count] !=
'/') {
349 std::stringstream ss; ss <<
"Invalid Content-Range value (no trailing /): " << value;
354 std::stringstream ss; ss <<
"Invalid Content-Range value (negative length): " << line;
357 if (length > std::numeric_limits<
decltype(
m_current_op.second)>::max()) {
358 std::stringstream ss; ss <<
"Invalid Content-Range value (length too long): " << line;
364 CalculateNextBuffer();
366 m_multipart_boundary =
true;
CurlOperation(XrdCl::ResponseHandler *handler, const std::string &url, struct timespec timeout, XrdCl::Log *log, CreateConnCalloutType, HeaderCallout *header_callout)
CurlVectorReadOp(XrdCl::ResponseHandler *handler, const std::string &url, struct timespec timeout, const XrdCl::ChunkList &op_list, XrdCl::Log *logger, CreateConnCalloutType callout, HeaderCallout *header_callout)