XRootD
Loading...
Searching...
No Matches
XrdClHttpOpTape.cc
Go to the documentation of this file.
1/******************************************************************************/
2/* */
3/* X r d C l H t t p O p T a p e . c c */
4/* */
5/* (c) 2026 by the XRootD Collaboration */
6/* */
7/* This file is part of the XRootD software suite. */
8/* */
9/* XRootD is free software: you can redistribute it and/or modify it under */
10/* the terms of the GNU Lesser General Public License as published by the */
11/* Free Software Foundation, either version 3 of the License, or (at your */
12/* option) any later version. */
13/* */
14/* XRootD is distributed in the hope that it will be useful, but WITHOUT */
15/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
16/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
17/* License for more details. */
18/* */
19/* You should have received a copy of the GNU Lesser General Public License */
20/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
21/* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
22/* */
23/******************************************************************************/
24
25#include "XrdClHttpOps.hh"
26#include "XrdClHttpTape.hh"
27#include "XrdClHttpWorker.hh"
28
30
31#include <cerrno>
32#include <exception>
33#include <stdexcept>
34
35using namespace XrdClHttp;
36
38 const std::string &url, std::unique_ptr<TapeOperation> tape,
39 struct timespec timeout, XrdCl::Log *logger,
40 CreateConnCalloutType callout, HeaderCallout *header_callout)
41:
42 CurlOperation(handler, url, timeout, logger, callout, header_callout),
43 m_tape(std::move(tape))
44{
45}
46
48 const std::string &url, const std::vector<std::string> &file_list,
49 XrdCl::PrepareFlags::Flags flags, struct timespec timeout,
50 XrdCl::Log *logger, CreateConnCalloutType callout,
51 HeaderCallout *header_callout)
52:
53 CurlTapeOp(handler, url,
54 std::make_unique<TapeOperation>(url, file_list, flags), timeout,
55 logger, callout, header_callout)
56{
57}
58
60 const std::string &url, XrdCl::QueryCode::Code query_code,
61 const XrdCl::Buffer &arg, struct timespec timeout,
62 XrdCl::Log *logger, CreateConnCalloutType callout,
63 HeaderCallout *header_callout)
64:
65 CurlTapeOp(handler, url,
66 std::make_unique<TapeOperation>(url, query_code, arg), timeout,
67 logger, callout, header_callout)
68{
69}
70
71CurlTapeOp::~CurlTapeOp() = default;
72
73bool
75{
76 const auto failure = [&](uint16_t code, uint32_t error,
77 const std::string &message) {
78 if(!m_curl) m_curl.reset(curl);
79 Fail(code, error, message);
80 return false;
81 };
82
83 try
84 {
85 const auto status = m_tape->Start(m_request);
86 if(!status.IsOK())
87 {
88 return failure(status.code, status.errNo,
89 status.GetErrorMessage());
90 }
91
92 m_request_url = m_request.url;
93 if(!CurlOperation::Setup(curl, worker))
94 {
95 throw std::runtime_error(
96 "failed to initialize " + RequestDescription());
97 }
98 m_worker = &worker;
99 if(!ConfigureRequest())
100 {
101 throw std::runtime_error(
102 "failed to configure " + RequestDescription());
103 }
104 return true;
105 }
106 catch(const std::exception &ex)
107 {
108 return failure(XrdCl::errInternal, EIO, ex.what());
109 }
110}
111
112bool
113CurlTapeOp::ConfigureRequest()
114{
115 if(!m_curl) return false;
116
117 m_response.clear();
118 m_headers_list.emplace_back("Accept", "application/json");
119
120 if(curl_easy_setopt(m_curl.get(), CURLOPT_WRITEFUNCTION,
121 CurlTapeOp::WriteCallback) != CURLE_OK
122 || curl_easy_setopt(m_curl.get(), CURLOPT_WRITEDATA, this) != CURLE_OK)
123 {
124 return false;
125 }
126#if CURL_AT_LEAST_VERSION(7, 85, 0)
127 if(curl_easy_setopt(m_curl.get(), CURLOPT_PROTOCOLS_STR, "https,http")
128 != CURLE_OK
129 || curl_easy_setopt(m_curl.get(), CURLOPT_REDIR_PROTOCOLS_STR,
130 "https,http") != CURLE_OK)
131 {
132 return false;
133 }
134#else
135 const long protocols = CURLPROTO_HTTP | CURLPROTO_HTTPS;
136 if(curl_easy_setopt(m_curl.get(), CURLOPT_PROTOCOLS, protocols) != CURLE_OK
137 || curl_easy_setopt(m_curl.get(), CURLOPT_REDIR_PROTOCOLS, protocols)
138 != CURLE_OK)
139 {
140 return false;
141 }
142#endif
143
144 switch(m_request.method)
145 {
146 case HttpVerb::GET:
147 if(curl_easy_setopt(m_curl.get(), CURLOPT_HTTPGET, 1L) != CURLE_OK)
148 return false;
149 break;
150 case HttpVerb::POST:
151 m_headers_list.emplace_back("Content-Type", "application/json");
152 if(curl_easy_setopt(m_curl.get(), CURLOPT_POST, 1L) != CURLE_OK
153 || curl_easy_setopt(m_curl.get(), CURLOPT_POSTFIELDS,
154 m_request.body.c_str()) != CURLE_OK
155 || curl_easy_setopt(m_curl.get(), CURLOPT_POSTFIELDSIZE_LARGE,
156 static_cast<curl_off_t>(m_request.body.size())) != CURLE_OK)
157 {
158 return false;
159 }
160 break;
161 case HttpVerb::DELETE:
162 if(curl_easy_setopt(m_curl.get(), CURLOPT_CUSTOMREQUEST, "DELETE")
163 != CURLE_OK)
164 return false;
165 break;
166 default:
167 return false;
168 }
169 return true;
170}
171
172void
174{
175 try
176 {
177 TapeHttpRequest nextRequest;
178 std::string response;
179 bool complete = false;
180 const auto status = m_tape->Advance(
181 m_curl.get(), GetStatusCode(), m_response,
182 nextRequest, response, complete);
183 if(!status.IsOK())
184 {
185 Fail(status.code, status.errNo, status.GetErrorMessage());
186 return;
187 }
188 if(complete)
189 {
190 Complete(response);
191 return;
192 }
193
194 m_request = std::move(nextRequest);
195 if(!m_worker)
196 {
197 throw std::runtime_error(
198 "worker unavailable before starting " + RequestDescription());
199 }
200 if(!SetupNextRequest(m_request.url, *m_worker))
201 {
202 throw std::runtime_error(
203 "failed to reset the curl handle for " + RequestDescription());
204 }
205 if(!ConfigureRequest())
206 {
207 throw std::runtime_error(
208 "failed to configure " + RequestDescription());
209 }
210 if(!FinishSetup(m_curl.get()))
211 {
212 throw std::runtime_error(
213 "failed to configure headers for " + RequestDescription());
214 }
215 }
216 catch(const std::exception &ex)
217 {
218 Fail(XrdCl::errInternal, EIO, ex.what());
219 }
220}
221
222void
223CurlTapeOp::Fail(uint16_t errCode, uint32_t errNum,
224 const std::string &message)
225{
226 std::string detail = message;
227 if(GetStatusCode() >= 400)
228 {
229 detail = RequestDescription() + " failed: "
230 + TapeProblemResponse(GetStatusCode(), m_response);
231 }
232 CurlOperation::Fail(errCode, errNum, detail);
233}
234
237{
238 return m_request.method;
239}
240
241std::string
242CurlTapeOp::RequestDescription() const
243{
244 return "Tape REST " + GetVerbString(GetVerb()) + " " + m_request.url;
245}
246
247void
249{
250 if(m_curl == nullptr) return;
251
252 curl_easy_setopt(m_curl.get(), CURLOPT_ERRORBUFFER, nullptr);
253 curl_easy_setopt(m_curl.get(), CURLOPT_WRITEFUNCTION, nullptr);
254 curl_easy_setopt(m_curl.get(), CURLOPT_WRITEDATA, nullptr);
255 curl_easy_setopt(m_curl.get(), CURLOPT_HTTPHEADER, nullptr);
256 curl_easy_setopt(m_curl.get(), CURLOPT_POST, 0L);
257 curl_easy_setopt(m_curl.get(), CURLOPT_POSTFIELDS, nullptr);
258 curl_easy_setopt(m_curl.get(), CURLOPT_POSTFIELDSIZE_LARGE,
259 static_cast<curl_off_t>(0));
260 curl_easy_setopt(m_curl.get(), CURLOPT_CUSTOMREQUEST, nullptr);
262}
263
264size_t
265CurlTapeOp::WriteCallback(char *buffer, size_t size, size_t nitems, void *data)
266{
267 return static_cast<CurlTapeOp *>(data)->Write(buffer, size * nitems);
268}
269
270size_t
271CurlTapeOp::Write(char *buffer, size_t size)
272{
273 try
274 {
275 m_response.append(buffer, size);
276 UpdateBytes(size);
277 return size;
278 }
279 catch(const std::exception &ex)
280 {
282 std::string("while buffering a Tape REST response: ") + ex.what());
283 }
284 catch(...)
285 {
287 "unknown exception while buffering a Tape REST response");
288 }
289}
290
291void
292CurlTapeOp::Complete(const std::string &response)
293{
294 if(m_handler == nullptr)
295 {
296 SetDone(false);
297 return;
298 }
299
300 auto buffer = std::make_unique<XrdCl::Buffer>();
301 buffer->FromString(response);
302
303 auto obj = std::make_unique<XrdCl::AnyObject>();
304 auto status = std::make_unique<XrdCl::XRootDStatus>();
305 obj->Set(buffer.release());
306
307 SetDone(false);
308 auto handle = m_handler;
309 m_handler = nullptr;
310 handle->HandleResponse(status.release(), obj.release());
311}
@ kXR_ServerError
void CURL
void SetDone(bool has_failed)
int FailCallback(XErrorCode ecode, const std::string &emsg)
bool FinishSetup(CURL *curl)
std::unique_ptr< CURL, void(*)(CURL *)> m_curl
static const std::string GetVerbString(HttpVerb)
virtual void ReleaseHandle()
void UpdateBytes(uint64_t bytes)
bool SetupNextRequest(const std::string &url, CurlWorker &worker)
std::vector< std::pair< std::string, std::string > > m_headers_list
XrdClHttp::HttpVerb HttpVerb
XrdCl::ResponseHandler * m_handler
CurlOperation(XrdCl::ResponseHandler *handler, const std::string &url, struct timespec timeout, XrdCl::Log *log, CreateConnCalloutType, HeaderCallout *header_callout)
virtual bool Setup(CURL *curl, CurlWorker &)
HttpVerb GetVerb() const override
bool Setup(CURL *curl, CurlWorker &worker) override
CurlTapeOp(XrdCl::ResponseHandler *handler, const std::string &url, std::unique_ptr< TapeOperation > tape, struct timespec timeout, XrdCl::Log *logger, CreateConnCalloutType callout, HeaderCallout *header_callout)
void Fail(uint16_t errCode, uint32_t errNum, const std::string &message) override
void ReleaseHandle() override
CurlTapePrepareOp(XrdCl::ResponseHandler *handler, const std::string &url, const std::vector< std::string > &file_list, XrdCl::PrepareFlags::Flags flags, struct timespec timeout, XrdCl::Log *logger, CreateConnCalloutType callout, HeaderCallout *header_callout)
CurlTapeQueryOp(XrdCl::ResponseHandler *handler, const std::string &url, XrdCl::QueryCode::Code query_code, const XrdCl::Buffer &arg, struct timespec timeout, XrdCl::Log *logger, CreateConnCalloutType callout, HeaderCallout *header_callout)
Binary blob representation.
Handle diagnostics.
Definition XrdClLog.hh:101
Handle an async response.
virtual void HandleResponse(XRootDStatus *status, AnyObject *response)
ConnectionCallout *(*)(const std::string &, const ResponseInfo &) CreateConnCalloutType
std::string TapeProblemResponse(long statusCode, const std::string &body)
const uint16_t errInternal
Internal error.
Code
XRootD query request codes.