XRootD
XrdClHttpFilesystem.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 "XrdClHttpFactory.hh"
22 #include "XrdClHttpFilesystem.hh"
23 #include "XrdClHttpOps.hh"
24 #include "XrdClHttpResponses.hh"
25 
26 using namespace XrdClHttp;
27 
28 Filesystem::Filesystem(const std::string &url, std::shared_ptr<HandlerQueue> queue, XrdCl::Log *log)
29  : m_queue(queue),
30  m_logger(log),
31  m_url(url)
32 {
33  m_logger->Debug(kLogXrdClHttp, "Constructing filesystem object with base URL %s", url.c_str());
34  // When constructed from the root protocol handler, we've observed it include the
35  // path here (the code paths appear to be slightly different from http://). Strip
36  // it out so it's not included twice later.
37  m_url.SetPath("/");
39  m_url.SetParams(map);
40 }
41 
42 Filesystem::~Filesystem() noexcept {}
43 
45 Filesystem::DirList(const std::string &path,
47  XrdCl::ResponseHandler *handler,
48  time_t timeout )
49 {
51 
52  auto full_url = GetCurrentURL(path);
53 
54  m_logger->Debug(kLogXrdClHttp, "Filesystem::DirList path %s", path.c_str());
55  std::unique_ptr<XrdClHttp::CurlListdirOp> listdirOp(
57  handler, full_url,
58  m_url.GetHostName() + ":" + std::to_string(m_url.GetPort()),
59  SendResponseInfo(), ts, m_logger,
60  GetConnCallout(), m_header_callout.load(std::memory_order_acquire)
61  )
62  );
63 
64  try {
65  m_queue->Produce(std::move(listdirOp));
66  } catch (...) {
67  m_logger->Warning(kLogXrdClHttp, "Failed to add dirlist op to queue");
69  }
70 
71  return XrdCl::XRootDStatus();
72 }
73 
74 CreateConnCalloutType
75 Filesystem::GetConnCallout() const {
76  std::string pointer_str;
77  if (!GetProperty("XrdClConnectionCallout", pointer_str) && pointer_str.empty()) {
78  return nullptr;
79  }
80  long long pointer;
81  try {
82  pointer = std::stoll(pointer_str, nullptr, 16);
83  } catch (...) {
84  return nullptr;
85  }
86  if (!pointer) {
87  return nullptr;
88  }
89  return reinterpret_cast<CreateConnCalloutType>(pointer);
90 }
91 
92 bool
93 Filesystem::GetProperty(const std::string &name,
94  std::string &value) const
95 {
96  std::shared_lock lock(m_properties_mutex);
97 
98  const auto p = m_properties.find(name);
99  if (p == std::end(m_properties)) {
100  return false;
101  }
102 
103  value = p->second;
104  return true;
105 }
106 
107 // Trivial implementation of the "locate" call
108 //
109 // On Linux, this is invoked by the XrdCl client prior to directory listings.
110 // Given there's no concept of multiple locations currently, we just return
111 // the original host and port as the available "location".
113 Filesystem::Locate( const std::string &path,
115  XrdCl::ResponseHandler *handler,
116  time_t timeout )
117 {
118  if (!handler) return XrdCl::XRootDStatus();
119 
120  auto locateInfo = std::make_unique<XrdCl::LocationInfo>();
121  locateInfo->Add(XrdCl::LocationInfo::Location(m_url.GetHostName() + ":" + std::to_string(m_url.GetPort()), XrdCl::LocationInfo::ServerOnline, XrdCl::LocationInfo::Read));
122 
123  auto obj = std::make_unique<XrdCl::AnyObject>();
124  obj->Set(locateInfo.release());
125  handler->HandleResponse(new XrdCl::XRootDStatus(), obj.release());
126 
127  return XrdCl::XRootDStatus();
128 }
129 
130 XrdCl::XRootDStatus Filesystem::MkDir(const std::string &path,
132  XrdCl::Access::Mode mode,
133  XrdCl::ResponseHandler *handler,
134  time_t timeout)
135 {
137 
138  auto full_url = GetCurrentURL(path);
139  m_logger->Debug(kLogXrdClHttp, "Filesystem::MkDir path %s", full_url.c_str());
140 
141  std::unique_ptr<CurlMkcolOp> mkdirOp(
142  new CurlMkcolOp(
143  handler, full_url, ts, m_logger, SendResponseInfo(), GetConnCallout(),
144  m_header_callout.load(std::memory_order_acquire)
145  )
146  );
147  try {
148  m_queue->Produce(std::move(mkdirOp));
149  } catch (...) {
150  m_logger->Warning(kLogXrdClHttp, "Failed to add filesystem mkdir op to queue");
152  }
153 
154  return XrdCl::XRootDStatus();
155 }
156 
158  const XrdCl::Buffer &arg,
159  XrdCl::ResponseHandler *handler,
160  time_t timeout)
161 {
163 
164  if (queryCode == XrdCl::QueryCode::Checksum)
165  {
166  auto url = GetCurrentURL(arg.ToString());
167  m_logger->Debug(kLogXrdClHttp, "XrdClHttp::Filesystem::Query checksum path %s", url.c_str());
168 
169  XrdClHttp::ChecksumType preferred = XrdClHttp::ChecksumType::kCRC32C;
170  XrdCl::URL url_obj;
171  url_obj.FromString(url);
172  auto iter = url_obj.GetParams().find("cks.type");
173  if (iter != url_obj.GetParams().end())
174  {
175  preferred = XrdClHttp::GetTypeFromString(iter->second);
176  if (preferred == XrdClHttp::ChecksumType::kUnknown)
177  {
178  m_logger->Error(kLogXrdClHttp, "Unknown checksum type %s", iter->second.c_str());
179  preferred = XrdClHttp::ChecksumType::kCRC32C;
180  }
181  }
182  // On miss, queue a checksum operation
183  std::unique_ptr<CurlChecksumOp> cksumOp(
184  new CurlChecksumOp(
185  handler, url, preferred, ts, m_logger, SendResponseInfo(),
186  GetConnCallout(), m_header_callout.load(std::memory_order_acquire)
187  )
188  );
189  try
190  {
191  m_queue->Produce(std::move(cksumOp));
192  }
193  catch (...)
194  {
195  m_logger->Warning(kLogXrdClHttp, "Failed to add checksum operation to queue");
197  }
198  }
199  else if (queryCode == XrdCl::QueryCode::XAttr)
200  {
201  std::string path = arg.ToString();
202  std::string full_url = m_url.GetURL();
203  m_logger->Debug(kLogXrdClHttp, "XrdClHttp::Filesystem::Query xattr full_url %s, path %s", full_url.c_str(), path.c_str());
204  full_url = m_url.GetURL();
205  std::unique_ptr<CurlQueryOp> queryOp(
206  new CurlQueryOp(
207  handler, path, ts, m_logger,SendResponseInfo(),
208  GetConnCallout(), queryCode, m_header_callout.load(std::memory_order_acquire)
209  )
210  );
211  try
212  {
213  m_queue->Produce(std::move(queryOp));
214  }
215  catch (...)
216  {
217  m_logger->Warning(kLogXrdClHttp, "Failed to add xattr query operation to queue");
219  }
220  }
221  else
222  {
224  }
225  return XrdCl::XRootDStatus();
226 }
227 
229 Filesystem::Rm(const std::string &path,
230  XrdCl::ResponseHandler *handler,
231  time_t timeout)
232 {
234 
235  auto full_url = GetCurrentURL(path);
236  m_logger->Debug(kLogXrdClHttp, "Filesystem::Rm path %s", full_url.c_str());
237 
238  std::unique_ptr<CurlDeleteOp> deleteOp(
239  new CurlDeleteOp(
240  handler, full_url, ts, m_logger, SendResponseInfo(),
241  GetConnCallout(), m_header_callout.load(std::memory_order_acquire)
242  )
243  );
244  try {
245  m_queue->Produce(std::move(deleteOp));
246  } catch (...) {
247  m_logger->Warning(kLogXrdClHttp, "Failed to add filesystem delete op to queue");
249  }
250 
251  return XrdCl::XRootDStatus();
252 }
253 
255 Filesystem::RmDir(const std::string &path,
256  XrdCl::ResponseHandler *handler,
257  time_t timeout)
258 {
259  return Rm(path, handler, timeout);
260 }
261 
262 bool
263 Filesystem::SetProperty(const std::string &name,
264  const std::string &value)
265 {
266  if (name == "XrdClHttpHeaderCallout") {
267  long long pointer;
268  try {
269  pointer = std::stoll(value, nullptr, 16);
270  } catch (...) {
271  pointer = 0;
272  }
273  if (!pointer) {
274  pointer = 0;
275  }
276  m_header_callout.store(reinterpret_cast<XrdClHttp::HeaderCallout*>(pointer), std::memory_order_release);
277  }
278 
279  std::unique_lock lock(m_properties_mutex);
280  m_properties[name] = value;
281  return true;
282 }
283 
285 Filesystem::Stat(const std::string &path,
286  XrdCl::ResponseHandler *handler,
287  time_t timeout)
288 {
290 
291  auto full_url = GetCurrentURL(path);
292  m_logger->Debug(kLogXrdClHttp, "Filesystem::Stat path %s", full_url.c_str());
293 
294  std::unique_ptr<CurlStatOp> statOp(
295  new CurlStatOp(
296  handler, full_url, ts, m_logger, SendResponseInfo(),
297  GetConnCallout(), m_header_callout.load(std::memory_order_acquire)
298  )
299  );
300  try {
301  m_queue->Produce(std::move(statOp));
302  } catch (...) {
303  m_logger->Warning(kLogXrdClHttp, "Failed to add filesystem stat op to queue");
305  }
306 
307  return XrdCl::XRootDStatus();
308 }
309 
310 bool Filesystem::SendResponseInfo() const {
311  std::string val;
312  return GetProperty(ResponseInfoProperty, val) && val == "true";
313 }
314 
315 std::string Filesystem::GetCurrentURL(const std::string &path) const {
316 
317  // Compute the URL without trailing slash.
318  auto prefix = m_url.GetURL();
319  std::string_view prefix_view = prefix;
320  while (!prefix_view.empty() && prefix_view[prefix_view.size() - 1] == '/')
321  prefix_view = prefix_view.substr(0, prefix_view.size() - 1);
322 
323  // Compute the target path without the '/' prefix
324  std::string_view path_view = path;
325  while (!path_view.empty() && path_view[0] == '/')
326  path_view = path_view.substr(1);
327  auto retval = std::string(prefix_view) + "/" + std::string(path_view);
328 
329  // Add in the query parameters, if relevant.
330  {
331  std::shared_lock lock(m_properties_mutex);
332  auto iter = m_properties.find("XrdClHttpQueryParam");
333  if (iter != m_properties.end() && !iter->second.empty()) {
334  retval += ((retval.find('?') == std::string::npos) ? '?' : ':') + iter->second;
335  }
336  }
337  return retval;
338 }
static std::string ts()
timestamp output for logging messages
Definition: XrdCephOss.cc:53
struct stat Stat
Definition: XrdCks.cc:49
#define ResponseInfoProperty
static struct timespec GetHeaderTimeoutWithDefault(time_t oper_timeout)
Filesystem(const std::string &, std::shared_ptr< HandlerQueue > queue, XrdCl::Log *log)
Binary blob representation.
Definition: XrdClBuffer.hh:34
std::string ToString() const
Convert the buffer to a string.
Definition: XrdClBuffer.hh:215
@ Read
read access is allowed
@ ServerOnline
server node where the file is online
Handle diagnostics.
Definition: XrdClLog.hh:101
Handle an async response.
virtual void HandleResponse(XRootDStatus *status, AnyObject *response)
URL representation.
Definition: XrdClURL.hh:31
std::map< std::string, std::string > ParamsMap
Definition: XrdClURL.hh:33
bool FromString(const std::string &url)
Parse a string and fill the URL fields.
Definition: XrdClURL.cc:62
const ParamsMap & GetParams() const
Get the URL params.
Definition: XrdClURL.hh:244
ChecksumType GetTypeFromString(const std::string &str)
MkDirImpl< false > MkDir
const uint16_t errNotImplemented
Operation is not implemented.
Definition: XrdClStatus.hh:64
RmImpl< false > Rm
const uint16_t stError
An error occurred that could potentially be retried.
Definition: XrdClStatus.hh:32
LocateImpl< false > Locate
const uint16_t errOSError
Definition: XrdClStatus.hh:61
RmDirImpl< false > RmDir
DirListImpl< false > DirList
QueryImpl< false > Query
Mode
Access mode.
Flags
Open flags, may be or'd when appropriate.
Code
XRootD query request codes.
@ XAttr
Query file extended attributes.
@ Checksum
Query file checksum.