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;
185 CalculateNextBuffer();
212 auto get_next_line = [&]() {
213 std::string_view chunk_header(buffer, length);
214 auto pos = chunk_header.find(
"\r\n");
215 if (pos == std::string_view::npos) {
218 return std::make_pair(std::string_view(),
false);
220 auto line = chunk_header.substr(0, pos);
227 return std::make_pair(line,
true);
232 bool last_segment =
false;
234 auto [line, ok] = get_next_line();
241 if (line.empty()) {
continue;}
249 std::stringstream ss;
250 ss <<
"Server has responded with an invalid boundary line: '" << line <<
"' (expected '" <<
m_headers.
MultipartSeparator() <<
"')";
259 auto [line, ok] = get_next_line();
266 auto header_name_end = line.find(
':');
267 if (header_name_end == std::string_view::npos) {
268 std::stringstream ss; ss <<
"Invalid header line in response from server: " << line;
271 auto header_name = line.substr(0, header_name_end);
274 if (header_name.size() != 13 || strncasecmp(header_name.data(),
"content-range", 13)) {
279 auto value = line.substr(header_name_end + 1);
282 while (!value.empty() && value[0] ==
' ') {
283 value = value.substr(1);
286 if (value.substr(0, 5) !=
"bytes") {
287 std::stringstream ss; ss <<
"Invalid Content-Range value (no 'bytes' unit): " << value;
291 value = value.substr(5);
292 while (!value.empty() && value[0] ==
' ') {
293 value = value.substr(1);
304 bytes_val = std::stoll(value.data(), &count);
305 }
catch (std::invalid_argument &) {
306 std::stringstream ss; ss <<
"Invalid Content-Range value (no integer in range start): " << value;
308 }
catch (std::out_of_range &) {
309 std::stringstream ss; ss <<
"Invalid Content-Range value (out of range): " << value;
312 if (value.size() <= count || value[count] !=
'-') {
313 std::stringstream ss; ss <<
"Invalid Content-Range value (no dash in range): " << value;
317 value = value.substr(count + 1);
319 bytes_val = std::stoll(value.data(), &count);
320 }
catch (std::invalid_argument &) {
321 std::stringstream ss; ss <<
"Invalid Content-Range value (no integer in range end): " << value;
323 }
catch (std::out_of_range &) {
324 std::stringstream ss; ss <<
"Invalid Content-Range value (out of range in range end): " << value;
327 if (value.size() <= count || value[count] !=
'/') {
328 std::stringstream ss; ss <<
"Invalid Content-Range value (no trailing /): " << value;
333 std::stringstream ss; ss <<
"Invalid Content-Range value (negative length): " << line;
336 if (length > std::numeric_limits<decltype(
m_current_op.second)>::max()) {
337 std::stringstream ss; ss <<
"Invalid Content-Range value (length too long): " << line;
343 CalculateNextBuffer();
353 void CurlVectorReadOp::CalculateNextBuffer() {
355 off_t distance = std::numeric_limits<off_t>::max();
366 if (bytes_to_skip > 0 && bytes_to_skip < distance) {
367 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
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