33 CurlOperation(handler, url, timeout, logger, callout, header_callout),
34 m_vr(new
XrdCl::VectorReadInfo()),
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);
100 obj->Set(
m_vr.release());
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);
121 CurlVectorReadOp::WriteCallback(
char *buffer,
size_t size,
size_t nitems,
void *this_ptr)
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;
166 auto to_copy = (
static_cast<size_t>(remaining) < length) ?
static_cast<size_t>(remaining) : length;
180 m_multipart_boundary =
true;
186 CalculateNextBuffer();
198 m_multipart_boundary =
true;
214 auto get_next_line = [&]() {
215 std::string_view chunk_header(buffer, length);
216 auto pos = chunk_header.find(
"\r\n");
217 if (pos == std::string_view::npos) {
220 return std::make_pair(std::string_view(),
false);
222 auto line_part = chunk_header.substr(0, pos);
231 return std::make_pair(std::string_view(
m_header_line),
true);
236 bool last_segment =
false;
237 if (m_multipart_boundary) {
239 auto [line, ok] = get_next_line();
246 if (line.empty()) {
continue;}
254 std::stringstream ss;
255 ss <<
"Server has responded with an invalid boundary line: '" << line <<
"' (expected '" <<
m_headers.
MultipartSeparator() <<
"')";
265 auto [line, ok] = get_next_line();
267 m_multipart_boundary =
false;
273 auto header_name_end = line.find(
':');
274 if (header_name_end == std::string_view::npos) {
275 std::stringstream ss; ss <<
"Invalid header line in response from server: " << line;
278 auto header_name = line.substr(0, header_name_end);
281 if (header_name.size() != 13 || strncasecmp(header_name.data(),
"content-range", 13)) {
286 auto value = line.substr(header_name_end + 1);
289 while (!value.empty() && value[0] ==
' ') {
290 value = value.substr(1);
293 if (value.substr(0, 5) !=
"bytes") {
294 std::stringstream ss; ss <<
"Invalid Content-Range value (no 'bytes' unit): " << value;
298 value = value.substr(5);
299 while (!value.empty() && value[0] ==
' ') {
300 value = value.substr(1);
311 bytes_val = std::stoll(value.data(), &count);
312 }
catch (std::invalid_argument &) {
313 std::stringstream ss; ss <<
"Invalid Content-Range value (no integer in range start): " << value;
315 }
catch (std::out_of_range &) {
316 std::stringstream ss; ss <<
"Invalid Content-Range value (out of range): " << value;
319 if (value.size() <= count || value[count] !=
'-') {
320 std::stringstream ss; ss <<
"Invalid Content-Range value (no dash in range): " << value;
324 value = value.substr(count + 1);
326 bytes_val = std::stoll(value.data(), &count);
327 }
catch (std::invalid_argument &) {
328 std::stringstream ss; ss <<
"Invalid Content-Range value (no integer in range end): " << value;
330 }
catch (std::out_of_range &) {
331 std::stringstream ss; ss <<
"Invalid Content-Range value (out of range in range end): " << value;
334 if (value.size() <= count || value[count] !=
'/') {
335 std::stringstream ss; ss <<
"Invalid Content-Range value (no trailing /): " << value;
340 std::stringstream ss; ss <<
"Invalid Content-Range value (negative length): " << line;
343 if (length > std::numeric_limits<decltype(
m_current_op.second)>::max()) {
344 std::stringstream ss; ss <<
"Invalid Content-Range value (length too long): " << line;
350 CalculateNextBuffer();
352 m_multipart_boundary =
true;
362 void CurlVectorReadOp::CalculateNextBuffer() {
364 off_t distance = std::numeric_limits<off_t>::max();
375 if (bytes_to_skip > 0 && bytes_to_skip < distance) {
376 distance = bytes_to_skip;
void SetDone(bool has_failed)
int FailCallback(XErrorCode ecode, const std::string &emsg)
int GetStatusCode() const
std::unique_ptr< CURL, void(*)(CURL *)> m_curl
virtual void ReleaseHandle()
void UpdateBytes(uint64_t bytes)
std::vector< std::pair< std::string, std::string > > m_headers_list
XrdCl::ResponseHandler * m_handler
virtual bool Setup(CURL *curl, CurlWorker &)
std::string m_response_headers
std::string m_header_line
size_t Write(char *buffer, size_t size)
CurlVectorReadOp(XrdCl::ResponseHandler *handler, const std::string &url, struct timespec timeout, const XrdCl::ChunkList &op_list, XrdCl::Log *logger, CreateConnCalloutType callout, HeaderCallout *header_callout)
void ReleaseHandle() override
XrdCl::ChunkList m_chunk_list
std::pair< off_t, off_t > m_current_op
std::unique_ptr< XrdCl::VectorReadInfo > m_vr
bool Setup(CURL *curl, CurlWorker &) override
void Fail(uint16_t errCode, uint32_t errNum, const std::string &msg) override
void Debug(uint64_t topic, const char *format,...)
Print a debug message.
Handle an async response.
virtual void HandleResponse(XRootDStatus *status, AnyObject *response)
bool HTTPStatusIsError(unsigned status)
const uint16_t stError
An error occurred that could potentially be retried.
std::vector< ChunkInfo > ChunkList
List of chunks.
ConnectionCallout *(*)(const std::string &, const ResponseInfo &) CreateConnCalloutType
const uint64_t kLogXrdClHttp