XRootD
XrdClHttpOpCopy.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* Copyright (C) 2025, Pelican Project, Morgridge Institute for Research */
3 /* */
4 /* This file is part of the XrdClHttp client plugin for XRootD. */
5 /* */
6 /* XRootD is free software: you can redistribute it and/or modify it under */
7 /* the terms of the GNU Lesser General Public License as published by the */
8 /* Free Software Foundation, either version 3 of the License, or (at your */
9 /* option) any later version. */
10 /* */
11 /* XRootD is distributed in the hope that it will be useful, but WITHOUT */
12 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
13 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
14 /* License for more details. */
15 /* */
16 /* The copyright holder's institutional names and contributor's names may not */
17 /* be used to endorse or promote products derived from this software without */
18 /* specific prior written permission of the institution or contributor. */
19 /******************************************************************************/
20 
21 #include "XrdClHttpOps.hh"
22 
23 using namespace XrdClHttp;
24 
25 CurlCopyOp::CurlCopyOp(XrdCl::ResponseHandler *handler, const std::string &source_url, const Headers &source_hdrs,
26  const std::string &dest_url, const Headers &dest_hdrs, struct timespec timeout, XrdCl::Log *logger,
27  CreateConnCalloutType callout) :
28  CurlOperation(handler, dest_url, timeout, logger, callout, nullptr),
29  m_source_url(source_url)
30  {
31  m_minimum_rate = 1;
32 
33  for (const auto &info : source_hdrs) {
34  m_headers_list.emplace_back(std::string("TransferHeader") + info.first, info.second);
35  }
36  for (const auto &info : dest_hdrs) {
37  m_headers_list.emplace_back(info.first, info.second);
38  }
39  }
40 
41  bool
43  {
44  auto rv = CurlOperation::Setup(curl, worker);
45  if (!rv) return false;
46 
47  curl_easy_setopt(m_curl.get(), CURLOPT_WRITEFUNCTION, CurlCopyOp::WriteCallback);
48  curl_easy_setopt(m_curl.get(), CURLOPT_WRITEDATA, this);
49  curl_easy_setopt(m_curl.get(), CURLOPT_CUSTOMREQUEST, "COPY");
50  m_headers_list.emplace_back("Source", m_source_url);
51 
52  return true;
53  }
54 
55  void
57  {
58  SetDone(false);
59  if (m_handler == nullptr) {return;}
60  auto status = new XrdCl::XRootDStatus();
61  auto obj = new XrdCl::AnyObject();
62  auto handle = m_handler;
63  m_handler = nullptr;
64  handle->HandleResponse(status, obj);
65  }
66 
67  void
69  {
70  if (m_curl == nullptr) return;
71  curl_easy_setopt(m_curl.get(), CURLOPT_WRITEFUNCTION, nullptr);
72  curl_easy_setopt(m_curl.get(), CURLOPT_WRITEDATA, nullptr);
73  curl_easy_setopt(m_curl.get(), CURLOPT_CUSTOMREQUEST, nullptr);
74  curl_easy_setopt(m_curl.get(), CURLOPT_HTTPHEADER, nullptr);
75  curl_easy_setopt(m_curl.get(), CURLOPT_XFERINFOFUNCTION, nullptr);
77  }
78 
79  size_t
80  CurlCopyOp::WriteCallback(char *buffer, size_t size, size_t nitems, void *this_ptr)
81  {
82  auto me = reinterpret_cast<CurlCopyOp*>(this_ptr);
83  me->UpdateBytes(size * nitems);
84  std::string_view str_data(buffer, size * nitems);
85  size_t end_line;
86  while ((end_line = str_data.find('\n')) != std::string_view::npos) {
87  auto cur_line = str_data.substr(0, end_line);
88  if (me->m_line_buffer.empty()) {
89  me->HandleLine(cur_line);
90  } else {
91  me->m_line_buffer += cur_line;
92  me->HandleLine(me->m_line_buffer);
93  me->m_line_buffer.clear();
94  }
95  str_data = str_data.substr(end_line + 1);
96  }
97  me->m_line_buffer = str_data;
98 
99  return size * nitems;
100  }
101 
102  void
103  CurlCopyOp::HandleLine(std::string_view line)
104  {
105  if (line == "Perf Marker") {
106  m_bytemark = -1;
107  } else if (line == "End") {
108  if (m_bytemark > -1 && m_callback) {
109  m_callback->Progress(m_bytemark);
110  }
111  } else {
112  auto key_end_pos = line.find(':');
113  if (key_end_pos == line.npos) {
114  return; // All the other callback lines should be of key: value format
115  }
116  auto key = line.substr(0, key_end_pos);
117  auto value = ltrim_view(line.substr(key_end_pos + 1));
118  if (key == "Stripe Bytes Transferred") {
119  try {
120  m_bytemark = std::stoll(std::string(value));
121  } catch (...) {
122  // TODO: Log failure
123  }
124  } else if (key == "success") {
125  m_sent_success = true;
126  } else if (key == "failure") {
127  m_failure = value;
128  }
129  }
130  }
131 
void CURL
void Success() override
void ReleaseHandle() override
CurlCopyOp(XrdCl::ResponseHandler *handler, const std::string &source_url, const Headers &source_hdrs, const std::string &dest_url, const Headers &dest_hdrs, struct timespec timeout, XrdCl::Log *logger, CreateConnCalloutType callout)
std::vector< std::pair< std::string, std::string > > Headers
bool Setup(CURL *curl, CurlWorker &) override
void SetDone(bool has_failed)
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 &)
Handle diagnostics.
Definition: XrdClLog.hh:101
Handle an async response.
virtual void HandleResponse(XRootDStatus *status, AnyObject *response)
std::string_view ltrim_view(const std::string_view &input_view)
ConnectionCallout *(*)(const std::string &, const ResponseInfo &) CreateConnCalloutType