XRootD
Loading...
Searching...
No Matches
XrdHttpTpcTPC.cc
Go to the documentation of this file.
4#include "XrdOuc/XrdOucEnv.hh"
8#include "XrdSys/XrdSysFD.hh"
9#include "XrdVersion.hh"
10
15
16#include <curl/curl.h>
17
18#include <dlfcn.h>
19#include <fcntl.h>
20
21#include <algorithm>
22#include <memory>
23#include <sstream>
24#include <stdexcept>
25#include <thread>
26
27#include "XrdHttpTpcState.hh"
28#include "XrdHttpTpcStream.hh"
29#include "XrdHttpTpcTPC.hh"
30#include <fstream>
31
32using namespace TPC;
33
34XrdXrootdTpcMon* TPCHandler::TPCLogRecord::tpcMonitor = 0;
35
36uint64_t TPCHandler::m_monid{0};
37int TPCHandler::m_marker_period = 5;
38size_t TPCHandler::m_block_size = 16*1024*1024;
39size_t TPCHandler::m_small_block_size = 1*1024*1024;
40XrdSysMutex TPCHandler::m_monid_mutex;
41bool TPCHandler::allowMissingCRL = false;
42
44
45/******************************************************************************/
46/* T P C H a n d l e r : : T P C L o g R e c o r d D e s t r u c t o r */
47/******************************************************************************/
48
49TPCHandler::TPCLogRecord::~TPCLogRecord()
50{
51// Record monitoring data is enabled
52//
53 if (tpcMonitor)
55
56 monInfo.clID = clID.c_str();
57 monInfo.begT = begT;
58 gettimeofday(&monInfo.endT, 0);
59
60 if (mTpcType == TpcType::Pull)
61 {monInfo.dstURL = local.c_str();
62 monInfo.srcURL = remote.c_str();
63 } else {
64 monInfo.dstURL = remote.c_str();
65 monInfo.srcURL = local.c_str();
67 }
68
69 if (!status) monInfo.endRC = 0;
70 else if (tpc_status > 0) monInfo.endRC = tpc_status;
71 else monInfo.endRC = 1;
72 monInfo.strm = static_cast<unsigned char>(streams);
73 monInfo.fSize = (bytes_transferred < 0 ? 0 : bytes_transferred);
74 if (!isIPv6) monInfo.opts |= XrdXrootdTpcMon::TpcInfo::isIPv4;
75
76 tpcMonitor->Report(monInfo);
77 }
78}
79
80/******************************************************************************/
81/* C u r l D e l e t e r : : o p e r a t o r ( ) */
82/******************************************************************************/
83
85{
86 if (curl) curl_easy_cleanup(curl);
87}
88
89/******************************************************************************/
90/* s o c k o p t _ s e t c l o e x e c _ c a l l b a c k */
91/******************************************************************************/
92
101int TPCHandler::sockopt_callback(void *clientp, curl_socket_t curlfd, curlsocktype purpose) {
102 TPCLogRecord * rec = (TPCLogRecord *)clientp;
103 if (purpose == CURLSOCKTYPE_IPCXN && rec && rec->pmarkManager.isEnabled()) {
104 // We will not reach this callback if the corresponding socket could not have been connected
105 // the socket is already connected only if the packet marking is enabled
106 return CURL_SOCKOPT_ALREADY_CONNECTED;
107 }
108 return CURL_SOCKOPT_OK;
109}
110
111/******************************************************************************/
112/* o p e n s o c k e t _ c a l l b a c k */
113/******************************************************************************/
114
115
120int TPCHandler::opensocket_callback(void *clientp,
121 curlsocktype purpose,
122 struct curl_sockaddr *aInfo)
123{
124 //Return a socket file descriptor (note the clo_exec flag will be set).
125 int fd = XrdSysFD_Socket(aInfo->family, aInfo->socktype, aInfo->protocol);
126 // See what kind of address will be used to connect
127 //
128 if(fd < 0) {
129 return CURL_SOCKET_BAD;
130 }
131 TPCLogRecord * rec = (TPCLogRecord *)clientp;
132 if (purpose == CURLSOCKTYPE_IPCXN && clientp)
133 {XrdNetAddr thePeer(&(aInfo->addr));
134 rec->isIPv6 = (thePeer.isIPType(XrdNetAddrInfo::IPv6)
135 && !thePeer.isMapped());
136 std::stringstream connectErrMsg;
137
138 if(!rec->pmarkManager.connect(fd, &(aInfo->addr), aInfo->addrlen, CONNECT_TIMEOUT, connectErrMsg)) {
139 rec->m_log->Emsg(rec->log_prefix.c_str(),"Unable to connect socket:", connectErrMsg.str().c_str());
140 return CURL_SOCKET_BAD;
141 }
142 }
143
144 return fd;
145}
146
147int TPCHandler::closesocket_callback(void *clientp, curl_socket_t fd) {
148 TPCLogRecord * rec = (TPCLogRecord *)clientp;
149
150 // Destroy the PMark handle associated to the file descriptor before closing it.
151 // Otherwise, we would lose the socket usage information if the socket is closed before
152 // the PMark handle is closed.
153 rec->pmarkManager.endPmark(fd);
154
155 return close(fd);
156}
157
158/******************************************************************************/
159/* s s l _ c t x _ c a l l b a c k */
160/******************************************************************************/
161
168int TPCHandler::ssl_ctx_callback(CURL *curl, void *ssl_ctx, void *clientp) {
169 //TPCLogRecord * rec = (TPCLogRecord *)clientp;
170 SSL_CTX* ctx = static_cast<SSL_CTX*>(ssl_ctx);
171 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback);
172 return CURL_SOCKOPT_OK;
173}
174
175int TPCHandler::verify_callback(int preverify_ok, X509_STORE_CTX* ctx) {
176 if (preverify_ok == 1) return 1;
177
178 int err = X509_STORE_CTX_get_error(ctx);
179
180 if (err == X509_V_ERR_UNABLE_TO_GET_CRL) {
181 X509_STORE_CTX_set_error(ctx, X509_V_OK);
182 return 1;
183 }
184
185 return 0;
186}
187
188/******************************************************************************/
189/* p r e p a r e U R L */
190/******************************************************************************/
191
192// See XrdHttpTpcUtils::prepareOpenURL() documentation
193std::string TPCHandler::prepareURL(XrdHttpExtReq &req) {
194 XrdHttpTpcUtils::PrepareOpenURLParams parms {req.resource, req.headers, hdr2cgimap,req.mReprDigest};
196}
197
198bool TPCHandler::mismatchReprDigest(const std::map<std::string, std::string> & passiveSrvReprDigest, XrdHttpExtReq &req,
199 TPCLogRecord &rec) {
200 if(passiveSrvReprDigest.size()) {
201 for (const auto & [digestName, digestValue]: passiveSrvReprDigest) {
202 auto clientDigestMatch = req.mReprDigest.find(digestName);
203 if (clientDigestMatch != req.mReprDigest.end()) {
204 // We found a checksum type match between the client-provided one and the source server-provided one
205 if (clientDigestMatch->second != digestValue) {
206 // The checksum value does not match, return an error to the client 412 PRECONDITION_FAILED
207 std::stringstream errMsg;
208 errMsg << "Mismatch between client-provided and remote server checksums:"
209 << " client = (" << clientDigestMatch->first << "=" << clientDigestMatch->second << ")"
210 << " server = (" << digestName << "=" << digestValue << ")";
211 logTransferEvent(LogMask::Error, rec, "REPRDIGEST_VERIFY_FAIL", errMsg.str());
212 rec.status=412;
213 req.SendSimpleResp(rec.status, NULL, NULL, generateClientErr(errMsg, rec, CURLcode::CURLE_OK).c_str(), 0);
214 return true;
215 }
216 }
217 }
218 }
219 return false;
220}
221
222/******************************************************************************/
223/* e n c o d e _ x r o o t d _ o p a q u e _ t o _ u r i */
224/******************************************************************************/
225
226// When processing a redirection from the filesystem layer, it is permitted to return
227// some xrootd opaque data. The quoting rules for xrootd opaque data are significantly
228// more permissive than a URI (basically, only '&' and '=' are disallowed while some
229// URI parsers may dislike characters like '"'). This function takes an opaque string
230// (e.g., foo=1&bar=2&baz=") and makes it safe for all URI parsers.
231std::string encode_xrootd_opaque_to_uri(CURL *curl, const std::string &opaque)
232{
233 std::stringstream parser(opaque);
234 std::string sequence;
235 std::stringstream output;
236 bool first = true;
237 while (getline(parser, sequence, '&')) {
238 if (sequence.empty()) {continue;}
239 size_t equal_pos = sequence.find('=');
240 char *val = NULL;
241 if (equal_pos != std::string::npos)
242 val = curl_easy_escape(curl, sequence.c_str() + equal_pos + 1, sequence.size() - equal_pos - 1);
243 // Do not emit parameter if value exists and escaping failed.
244 if (!val && equal_pos != std::string::npos) {continue;}
245
246 if (!first) output << "&";
247 first = false;
248 output << sequence.substr(0, equal_pos);
249 if (val) {
250 output << "=" << val;
251 curl_free(val);
252 }
253 }
254 return output.str();
255}
256
257/******************************************************************************/
258/* T P C H a n d l e r : : C o n f i g u r e C u r l C A */
259/******************************************************************************/
260
261void
262TPCHandler::ConfigureCurlCA(CURL *curl)
263{
264 auto ca_filename = m_ca_file ? m_ca_file->CAFilename() : "";
265 auto crl_filename = m_ca_file ? m_ca_file->CRLFilename() : "";
266 if (!ca_filename.empty() && !crl_filename.empty()) {
267 curl_easy_setopt(curl, CURLOPT_CAINFO, ca_filename.c_str());
268 //Check that the CRL file contains at least one entry before setting this option to curl
269 //Indeed, an empty CRL file will make curl unhappy and therefore will fail
270 //all HTTP TPC transfers (https://github.com/xrootd/xrootd/issues/1543)
271 std::ifstream in(crl_filename, std::ifstream::ate | std::ifstream::binary);
272 if(in.tellg() > 0 && m_ca_file->atLeastOneValidCRLFound()){
273 curl_easy_setopt(curl, CURLOPT_CRLFILE, crl_filename.c_str());
274 if (allowMissingCRL) {
275 // No need to set the callback if there is no need to do it
276 curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, ssl_ctx_callback);
277 }
278 } else {
279 std::ostringstream oss;
280 oss << "No valid CRL file has been found in the file " << crl_filename << ". Disabling CRL checking.";
281 m_log.Log(Warning,"TpcHandler",oss.str().c_str());
282 }
283 }
284 else if (!m_cadir.empty()) {
285 curl_easy_setopt(curl, CURLOPT_CAPATH, m_cadir.c_str());
286 }
287 if (!m_cafile.empty()) {
288 curl_easy_setopt(curl, CURLOPT_CAINFO, m_cafile.c_str());
289 }
290}
291
292
293bool TPCHandler::MatchesPath(const char *verb, const char *path) {
294 return !strcmp(verb, "COPY") || !strcmp(verb, "OPTIONS");
295}
296
297/******************************************************************************/
298/* P r e p a r e U R L */
299/******************************************************************************/
300
301static std::string PrepareURL(const std::string &input) {
302 if (!strncmp(input.c_str(), "davs://", 7)) {
303 return "https://" + input.substr(7);
304 }
305 return input;
306}
307
308/******************************************************************************/
309/* T P C H a n d l e r : : P r o c e s s R e q */
310/******************************************************************************/
311
313 if (req.verb == "OPTIONS") {
314 return ProcessOptionsReq(req);
315 }
316 auto header = XrdOucTUtils::caseInsensitiveFind(req.headers,"credential");
317 if (header != req.headers.end()) {
318 if (header->second != "none") {
319 m_log.Emsg("ProcessReq", "COPY requested an unsupported credential type: ", header->second.c_str());
320 return req.SendSimpleResp(400, NULL, NULL, "COPY requestd an unsupported Credential type", 0);
321 }
322 }
323 header = XrdOucTUtils::caseInsensitiveFind(req.headers,"source");
324 if (header != req.headers.end()) {
325 std::string src = PrepareURL(header->second);
326 return ProcessPullReq(src, req);
327 }
328 header = XrdOucTUtils::caseInsensitiveFind(req.headers,"destination");
329 if (header != req.headers.end()) {
330 return ProcessPushReq(header->second, req);
331 }
332 m_log.Emsg("ProcessReq", "COPY verb requested but no source or destination specified.");
333 return req.SendSimpleResp(400, NULL, NULL, "No Source or Destination specified", 0);
334}
335
336/******************************************************************************/
337/* T P C H a n d l e r D e s t r u c t o r */
338/******************************************************************************/
339
341 m_sfs = NULL;
342}
343
344/******************************************************************************/
345/* T P C H a n d l e r C o n s t r u c t o r */
346/******************************************************************************/
347
348TPCHandler::TPCHandler(XrdSysError *log, const char *config, XrdOucEnv *myEnv) :
349 m_desthttps(false),
350 m_fixed_route(false),
351 m_timeout(60),
352 m_first_timeout(120),
353 m_log(log->logger(), "TPC_"),
354 m_sfs(NULL)
355{
356 if (!Configure(config, myEnv)) {
357 throw std::runtime_error("Failed to configure the HTTP third-party-copy handler.");
358 }
359
360// Extract out the TPC monitoring object (we share it with xrootd).
361//
362 XrdXrootdGStream *gs = (XrdXrootdGStream*)myEnv->GetPtr("Tpc.gStream*");
363 if (gs)
364 TPCLogRecord::tpcMonitor = new XrdXrootdTpcMon("http",log->logger(),*gs);
365}
366
367/******************************************************************************/
368/* T P C H a n d l e r : : P r o c e s s O p t i o n s R e q */
369/******************************************************************************/
370
374int TPCHandler::ProcessOptionsReq(XrdHttpExtReq &req) {
375 return req.SendSimpleResp(200, NULL, (char *) "DAV: 1\r\nDAV: <http://apache.org/dav/propset/fs/1>\r\nAllow: HEAD,GET,PUT,PROPFIND,DELETE,OPTIONS,COPY", NULL, 0);
376}
377
378/******************************************************************************/
379/* T P C H a n d l e r : : G e t A u t h z */
380/******************************************************************************/
381
382std::string TPCHandler::GetAuthz(XrdHttpExtReq &req) {
383 std::string authz;
384 auto authz_header = XrdOucTUtils::caseInsensitiveFind(req.headers,"authorization");
385 if (authz_header != req.headers.end()) {
386 std::stringstream ss;
387 ss << "authz=" << encode_str(authz_header->second);
388 authz += ss.str();
389 }
390 return authz;
391}
392
393/******************************************************************************/
394/* T P C H a n d l e r : : R e d i r e c t T r a n s f e r */
395/******************************************************************************/
396
397int TPCHandler::RedirectTransfer(CURL *curl, const std::string &redirect_resource,
398 XrdHttpExtReq &req, XrdOucErrInfo &error, TPCLogRecord &rec)
399{
400 int port;
401 const char *ptr = error.getErrText(port);
402 if ((ptr == NULL) || (*ptr == '\0') || (port == 0)) {
403 rec.status = 500;
404 std::stringstream ss;
405 ss << "Internal error: redirect without hostname";
406 logTransferEvent(LogMask::Error, rec, "REDIRECT_INTERNAL_ERROR", ss.str());
407 return req.SendSimpleResp(rec.status, NULL, NULL, generateClientErr(ss, rec).c_str(), 0);
408 }
409
410 // Construct redirection URL taking into consideration any opaque info
411 std::string rdr_info = ptr;
412 std::string host, opaque;
413 size_t pos = rdr_info.find('?');
414 host = rdr_info.substr(0, pos);
415
416 if (pos != std::string::npos) {
417 opaque = rdr_info.substr(pos + 1);
418 }
419
420 std::stringstream ss;
421 ss << "Location: http" << (m_desthttps ? "s" : "") << "://" << host << ":" << port << "/" << redirect_resource;
422
423 if (!opaque.empty()) {
424 ss << "?" << encode_xrootd_opaque_to_uri(curl, opaque);
425 }
426
427 rec.status = 307;
428 logTransferEvent(LogMask::Info, rec, "REDIRECT", ss.str());
429 return req.SendSimpleResp(rec.status, NULL, const_cast<char *>(ss.str().c_str()),
430 NULL, 0);
431}
432
433/******************************************************************************/
434/* T P C H a n d l e r : : O p e n W a i t S t a l l */
435/******************************************************************************/
436
437int TPCHandler::OpenWaitStall(XrdSfsFile &fh, const std::string &resource,
438 int mode, int openMode, const XrdSecEntity &sec,
439 const std::string &authz)
440{
441 int open_result;
442 while (1) {
443 int orig_ucap = fh.error.getUCap();
444 fh.error.setUCap(orig_ucap | XrdOucEI::uIPv64);
445 std::string opaque;
446 size_t pos = resource.find('?');
447 // Extract the path and opaque info from the resource
448 std::string path = resource.substr(0, pos);
449
450 if (pos != std::string::npos) {
451 opaque = resource.substr(pos + 1);
452 }
453
454 // Append the authz information if there are some
455 if(!authz.empty()) {
456 opaque += (opaque.empty() ? "" : "&");
457 opaque += authz;
458 }
459 open_result = fh.open(path.c_str(), mode, openMode, &sec, opaque.c_str());
460
461 if ((open_result == SFS_STALL) || (open_result == SFS_STARTED)) {
462 int secs_to_stall = fh.error.getErrInfo();
463 if (open_result == SFS_STARTED) {secs_to_stall = secs_to_stall/2 + 5;}
464 std::this_thread::sleep_for (std::chrono::seconds(secs_to_stall));
465 }
466 break;
467 }
468 return open_result;
469}
470
471/******************************************************************************/
472/* T P C H a n d l e r : : D e t e r m i n e X f e r S i z e */
473/******************************************************************************/
474
475
476
480int TPCHandler::PerformHEADRequest(CURL *curl, XrdHttpExtReq &req, State &state,
481 bool &success, TPCLogRecord &rec, bool shouldReturnErrorToClient) {
482 success = false;
483 curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
484 // Set a custom timeout of 60 seconds (= CONNECT_TIMEOUT for convenience) for the HEAD request
485 curl_easy_setopt(curl, CURLOPT_TIMEOUT, CONNECT_TIMEOUT);
486 CURLcode res;
487 res = curl_easy_perform(curl);
488 //Immediately set the CURLOPT_NOBODY flag to 0 as we anyway
489 //don't want the next curl call to do be a HEAD request
490 curl_easy_setopt(curl, CURLOPT_NOBODY, 0);
491 // Reset the CURLOPT_TIMEOUT to no timeout (default)
492 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 0L);
493 if (res == CURLE_HTTP_RETURNED_ERROR) {
494 std::stringstream ss;
495 ss << "Remote server failed request while fetching file information (HEAD)";
496 std::stringstream ss2;
497 ss2 << ss.str() << ": " << curl_easy_strerror(res);
498 rec.status = 500;
499 logTransferEvent(LogMask::Error, rec, "HEAD_FAIL", ss2.str());
500 return shouldReturnErrorToClient ? req.SendSimpleResp(rec.status, NULL, NULL, generateClientErr(ss, rec, res).c_str(), 0) : -1;
501 } else if (state.GetStatusCode() >= 400) {
502 std::stringstream ss;
503 ss << "Remote side " << req.clienthost << " failed with status code " << state.GetStatusCode() << " while fetching remote file information (HEAD)";
504 rec.status = 500;
505 logTransferEvent(LogMask::Error, rec, "HEAD_FAIL", ss.str());
506 return shouldReturnErrorToClient ? req.SendSimpleResp(rec.status, NULL, NULL, generateClientErr(ss, rec).c_str(), 0) : -1;
507 } else if (res) {
508 std::stringstream ss;
509 ss << "Internal transfer failure while fetching remote file information (HEAD)";
510 std::stringstream ss2;
511 ss2 << ss.str() << " - HTTP library failed: " << curl_easy_strerror(res);
512 rec.status = 500;
513 logTransferEvent(LogMask::Error, rec, "HEAD_FAIL", ss2.str());
514 return shouldReturnErrorToClient ? req.SendSimpleResp(rec.status, NULL, NULL, generateClientErr(ss, rec, res).c_str(), 0) : -1;
515 }
516 std::stringstream ss;
517 ss << "Successfully determined remote file information for pull request: "
518 << "size=" << state.GetContentLength();
519 if(state.GetReprDigest().size()) {
520 unsigned int cksumIndex = 1;
521 for(const auto & [cksumType,cksumValue]: state.GetReprDigest()) {
522 ss << " chksum" << cksumIndex << "=(" << cksumType << "," << cksumValue << ")";
523 cksumIndex++;
524 }
525 }
526 logTransferEvent(LogMask::Debug, rec, "HEAD_SUCCESS", ss.str());
527 success = true;
528 return 0;
529}
530
531int TPCHandler::GetRemoteFileInfoTPCPull(CURL *curl, XrdHttpExtReq &req, uint64_t &contentLength, std::map<std::string,std::string> & reprDigest, bool & success, TPCLogRecord &rec) {
532 State state(curl,req.tpcForwardCreds);
533 //Don't forget to copy the headers of the client's request before doing the HEAD call. Otherwise, if there is a need for authentication,
534 //it will fail
535 state.SetupHeadersForHEAD(req);
536 int result;
537 //In case we cannot get the file HEAD request, we return the error to the client
538 if ((result = PerformHEADRequest(curl, req, state, success, rec)) || !success) {
539 return result;
540 }
541 contentLength = state.GetContentLength();
542 reprDigest = state.GetReprDigest();
543 return result;
544}
545
546/******************************************************************************/
547/* T P C H a n d l e r : : S e n d P e r f M a r k e r */
548/******************************************************************************/
549
550int TPCHandler::SendPerfMarker(XrdHttpExtReq &req, TPCLogRecord &rec, TPC::State &state) {
551 std::stringstream ss;
552 const std::string crlf = "\n";
553 ss << "Perf Marker" << crlf;
554 ss << "Timestamp: " << time(NULL) << crlf;
555 ss << "Stripe Index: 0" << crlf;
556 ss << "Stripe Bytes Transferred: " << state.BytesTransferred() << crlf;
557 ss << "Total Stripe Count: 1" << crlf;
558 // Include the TCP connection associated with this transfer; used by
559 // the TPC client for monitoring purposes.
560 std::string desc = state.GetConnectionDescription();
561 if (!desc.empty())
562 ss << "RemoteConnections: " << desc << crlf;
563 ss << "End" << crlf;
564 rec.bytes_transferred = state.BytesTransferred();
565 logTransferEvent(LogMask::Debug, rec, "PERF_MARKER");
566
567 return req.ChunkResp(ss.str().c_str(), 0);
568}
569
570/******************************************************************************/
571/* T P C H a n d l e r : : S e n d P e r f M a r k e r */
572/******************************************************************************/
573
574int TPCHandler::SendPerfMarker(XrdHttpExtReq &req, TPCLogRecord &rec, std::vector<State*> &state,
575 off_t bytes_transferred)
576{
577 // The 'performance marker' format is largely derived from how GridFTP works
578 // (e.g., the concept of `Stripe` is not quite so relevant here). See:
579 // https://twiki.cern.ch/twiki/bin/view/LCG/HttpTpcTechnical
580 // Example marker:
581 // Perf Marker\n
582 // Timestamp: 1537788010\n
583 // Stripe Index: 0\n
584 // Stripe Bytes Transferred: 238745\n
585 // Total Stripe Count: 1\n
586 // RemoteConnections: tcp:129.93.3.4:1234,tcp:[2600:900:6:1301:268a:7ff:fef6:a590]:2345\n
587 // End\n
588 //
589 std::stringstream ss;
590 const std::string crlf = "\n";
591 ss << "Perf Marker" << crlf;
592 ss << "Timestamp: " << time(NULL) << crlf;
593 ss << "Stripe Index: 0" << crlf;
594 ss << "Stripe Bytes Transferred: " << bytes_transferred << crlf;
595 ss << "Total Stripe Count: 1" << crlf;
596 // Build a list of TCP connections associated with this transfer; used by
597 // the TPC client for monitoring purposes.
598 bool first = true;
599 std::stringstream ss2;
600 for (std::vector<State*>::const_iterator iter = state.begin();
601 iter != state.end(); iter++)
602 {
603 std::string desc = (*iter)->GetConnectionDescription();
604 if (!desc.empty()) {
605 ss2 << (first ? "" : ",") << desc;
606 first = false;
607 }
608 }
609 if (!first)
610 ss << "RemoteConnections: " << ss2.str() << crlf;
611 ss << "End" << crlf;
612 rec.bytes_transferred = bytes_transferred;
613 logTransferEvent(LogMask::Debug, rec, "PERF_MARKER");
614
615 return req.ChunkResp(ss.str().c_str(), 0);
616}
617
618/******************************************************************************/
619/* T P C H a n d l e r : : R u n C u r l W i t h U p d a t e s */
620/******************************************************************************/
621
622int TPCHandler::RunCurlWithUpdates(CURL *curl, XrdHttpExtReq &req, State &state,
623 TPCLogRecord &rec)
624{
625 // Create the multi-handle and add in the current transfer to it.
626 CURLM *multi_handle = curl_multi_init();
627 if (!multi_handle) {
628 rec.status = 500;
629 logTransferEvent(LogMask::Error, rec, "CURL_INIT_FAIL",
630 "Failed to initialize a libcurl multi-handle");
631 std::stringstream ss;
632 ss << "Failed to initialize internal server memory";
633 return req.SendSimpleResp(rec.status, NULL, NULL, generateClientErr(ss, rec).c_str(), 0);
634 }
635
636 //curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 128*1024);
637
638 CURLMcode mres;
639 mres = curl_multi_add_handle(multi_handle, curl);
640 if (mres) {
641 rec.status = 500;
642 std::stringstream ss;
643 ss << "Failed to add transfer to libcurl multi-handle: HTTP library failure=" << curl_multi_strerror(mres);
644 logTransferEvent(LogMask::Error, rec, "CURL_INIT_FAIL", ss.str());
645 curl_multi_cleanup(multi_handle);
646 return req.SendSimpleResp(rec.status, NULL, NULL, generateClientErr(ss, rec).c_str(), 0);
647 }
648
649 // Start response to client prior to the first call to curl_multi_perform
650 int retval = req.StartChunkedResp(202, NULL, "Content-Type: text/plain");
651 if (retval) {
652 curl_multi_cleanup(multi_handle);
653 logTransferEvent(LogMask::Error, rec, "RESPONSE_FAIL",
654 "Failed to send the initial response to the TPC client");
655 return retval;
656 } else {
657 logTransferEvent(LogMask::Debug, rec, "RESPONSE_START",
658 "Initial transfer response sent to the TPC client");
659 }
660
661 // Transfer loop: use curl to actually run the transfer, but periodically
662 // interrupt things to send back performance updates to the client.
663 int running_handles = 1;
664 time_t last_marker = 0;
665 // Track how long it's been since the last time we recorded more bytes being transferred.
666 off_t last_advance_bytes = 0;
667 time_t last_advance_time = time(NULL);
668 time_t transfer_start = last_advance_time;
669 CURLcode res = static_cast<CURLcode>(-1);
670 do {
671 time_t now = time(NULL);
672 time_t next_marker = last_marker + m_marker_period;
673 if (now >= next_marker) {
674 off_t bytes_xfer = state.BytesTransferred();
675 if (bytes_xfer > last_advance_bytes) {
676 last_advance_bytes = bytes_xfer;
677 last_advance_time = now;
678 }
679 if (SendPerfMarker(req, rec, state)) {
680 curl_multi_remove_handle(multi_handle, curl);
681 curl_multi_cleanup(multi_handle);
682 logTransferEvent(LogMask::Error, rec, "PERFMARKER_FAIL",
683 "Failed to send a perf marker to the TPC client");
684 return -1;
685 }
686 int timeout = (transfer_start == last_advance_time) ? m_first_timeout : m_timeout;
687 if (now > last_advance_time + timeout) {
688 const char *log_prefix = rec.log_prefix.c_str();
689 bool tpc_pull = strncmp("Pull", log_prefix, 4) == 0;
690
691 state.SetErrorCode(10);
692 std::stringstream ss;
693 ss << "Transfer failed because no bytes have been "
694 << (tpc_pull ? "received from the source (pull mode) in "
695 : "transmitted to the destination (push mode) in ") << timeout << " seconds.";
696 state.SetErrorMessage(ss.str());
697 curl_multi_remove_handle(multi_handle, curl);
698 curl_multi_cleanup(multi_handle);
699 break;
700 }
701 last_marker = now;
702 }
703 // The transfer will start after this point, notify the packet marking manager
704 rec.pmarkManager.startTransfer();
705 mres = curl_multi_perform(multi_handle, &running_handles);
706 if (mres == CURLM_CALL_MULTI_PERFORM) {
707 // curl_multi_perform should be called again immediately. On newer
708 // versions of curl, this is no longer used.
709 continue;
710 } else if (mres != CURLM_OK) {
711 break;
712 } else if (running_handles == 0) {
713 break;
714 }
715
716 rec.pmarkManager.beginPMarks();
717 //printf("There are %d running handles\n", running_handles);
718
719 // Harvest any messages, looking for CURLMSG_DONE.
720 CURLMsg *msg;
721 do {
722 int msgq = 0;
723 msg = curl_multi_info_read(multi_handle, &msgq);
724 if (msg && (msg->msg == CURLMSG_DONE)) {
725 CURL *easy_handle = msg->easy_handle;
726 res = msg->data.result;
727 curl_multi_remove_handle(multi_handle, easy_handle);
728 }
729 } while (msg);
730
731 int64_t max_sleep_time = next_marker - time(NULL);
732 if (max_sleep_time <= 0) {
733 continue;
734 }
735 int fd_count;
736 mres = curl_multi_wait(multi_handle, NULL, 0, max_sleep_time*1000, &fd_count);
737 if (mres != CURLM_OK) {
738 break;
739 }
740 } while (running_handles);
741
742 if (mres != CURLM_OK) {
743 std::stringstream ss;
744 ss << "Internal libcurl multi-handle error: HTTP library failure=" << curl_multi_strerror(mres);
745 logTransferEvent(LogMask::Error, rec, "TRANSFER_CURL_ERROR", ss.str());
746
747 curl_multi_remove_handle(multi_handle, curl);
748 curl_multi_cleanup(multi_handle);
749
750 if ((retval = req.ChunkResp(generateClientErr(ss, rec).c_str(), 0))) {
751 logTransferEvent(LogMask::Error, rec, "RESPONSE_FAIL",
752 "Failed to send error message to the TPC client");
753 return retval;
754 }
755 return req.ChunkResp(NULL, 0);
756 }
757
758 // Harvest any messages, looking for CURLMSG_DONE.
759 CURLMsg *msg;
760 do {
761 int msgq = 0;
762 msg = curl_multi_info_read(multi_handle, &msgq);
763 if (msg && (msg->msg == CURLMSG_DONE)) {
764 CURL *easy_handle = msg->easy_handle;
765 res = msg->data.result;
766 curl_multi_remove_handle(multi_handle, easy_handle);
767 }
768 } while (msg);
769
770 if (!state.GetErrorCode() && res == static_cast<CURLcode>(-1)) { // No transfers returned?!?
771 curl_multi_remove_handle(multi_handle, curl);
772 curl_multi_cleanup(multi_handle);
773 std::stringstream ss;
774 ss << "Internal state error in libcurl";
775 logTransferEvent(LogMask::Error, rec, "TRANSFER_CURL_ERROR", ss.str());
776
777 if ((retval = req.ChunkResp(generateClientErr(ss, rec).c_str(), 0))) {
778 logTransferEvent(LogMask::Error, rec, "RESPONSE_FAIL",
779 "Failed to send error message to the TPC client");
780 return retval;
781 }
782 return req.ChunkResp(NULL, 0);
783 }
784 curl_multi_cleanup(multi_handle);
785
786 state.Flush();
787
788 rec.bytes_transferred = state.BytesTransferred();
789 rec.tpc_status = state.GetStatusCode();
790
791 // Explicitly finalize the stream (which will close the underlying file
792 // handle) before the response is sent. In some cases, subsequent HTTP
793 // requests can occur before the filesystem is done closing the handle -
794 // and those requests may occur against partial data.
795 state.Finalize();
796
797 // Generate the final response back to the client.
798 std::stringstream ss;
799 bool success = false;
800 if (state.GetStatusCode() >= 400) {
801 std::string err = state.GetErrorMessage();
802 std::stringstream ss2;
803 ss2 << "Remote side failed with status code " << state.GetStatusCode();
804 if (!err.empty()) {
805 std::replace(err.begin(), err.end(), '\n', ' ');
806 ss2 << "; error message: \"" << err << "\"";
807 }
808 logTransferEvent(LogMask::Error, rec, "TRANSFER_FAIL", ss2.str());
809 ss << generateClientErr(ss2, rec);
810 } else if (state.GetErrorCode()) {
811 std::string err = state.GetErrorMessage();
812 if (err.empty()) {err = "(no error message provided)";}
813 else {std::replace(err.begin(), err.end(), '\n', ' ');}
814 std::stringstream ss2;
815 ss2 << "Error when interacting with local filesystem: " << err;
816 logTransferEvent(LogMask::Error, rec, "TRANSFER_FAIL", ss2.str());
817 ss << generateClientErr(ss2, rec);
818 } else if (res != CURLE_OK) {
819 std::stringstream ss2;
820 ss2 << "Internal transfer failure";
821 std::stringstream ss3;
822 ss3 << ss2.str() << ": " << curl_easy_strerror(res);
823 logTransferEvent(LogMask::Error, rec, "TRANSFER_FAIL", ss3.str());
824 ss << generateClientErr(ss2, rec, res);
825 } else {
826 ss << "success: Created";
827 success = true;
828 }
829
830 if ((retval = req.ChunkResp(ss.str().c_str(), 0))) {
831 logTransferEvent(LogMask::Error, rec, "TRANSFER_ERROR",
832 "Failed to send last update to remote client");
833 return retval;
834 } else if (success) {
835 logTransferEvent(LogMask::Info, rec, "TRANSFER_SUCCESS");
836 rec.status = 0;
837 }
838 return req.ChunkResp(NULL, 0);
839}
840
841/******************************************************************************/
842/* T P C H a n d l e r : : P r o c e s s P u s h R e q */
843/******************************************************************************/
844
845int TPCHandler::ProcessPushReq(const std::string & resource, XrdHttpExtReq &req) {
846 TPCLogRecord rec(req, TpcType::Push);
847 rec.log_prefix = "PushRequest";
848 rec.local = req.resource;
849 rec.remote = resource;
850 rec.m_log = &m_log;
851 char *name = req.GetSecEntity().name;
852 req.GetClientID(rec.clID);
853 if (name) rec.name = name;
854 logTransferEvent(LogMask::Info, rec, "PUSH_START", "Starting a push request");
855
856 ManagedCurlHandle curlPtr(curl_easy_init());
857 auto curl = curlPtr.get();
858 if (!curl) {
859 std::stringstream ss;
860 ss << "Failed to initialize internal transfer resources";
861 rec.status = 500;
862 logTransferEvent(LogMask::Error, rec, "PUSH_FAIL", ss.str());
863 return req.SendSimpleResp(rec.status, NULL, NULL, generateClientErr(ss, rec).c_str(), 0);
864 }
865 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
866 curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, (long) CURL_HTTP_VERSION_1_1);
867// curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_setcloexec_callback);
868
869 curl_easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, opensocket_callback);
870 curl_easy_setopt(curl, CURLOPT_OPENSOCKETDATA, &rec);
871 curl_easy_setopt(curl, CURLOPT_CLOSESOCKETFUNCTION, closesocket_callback);
872 curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_callback);
873 curl_easy_setopt(curl, CURLOPT_CLOSESOCKETDATA, &rec);
874 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, CONNECT_TIMEOUT);
875 auto query_header = XrdOucTUtils::caseInsensitiveFind(req.headers,"xrd-http-fullresource");
876 std::string redirect_resource = req.resource;
877 if (query_header != req.headers.end()) {
878 redirect_resource = query_header->second;
879 }
880
881 AtomicBeg(m_monid_mutex);
882 uint64_t file_monid = AtomicInc(m_monid);
883 AtomicEnd(m_monid_mutex);
884 std::unique_ptr<XrdSfsFile> fh(m_sfs->newFile(name, file_monid));
885 if (!fh.get()) {
886 rec.status = 500;
887 std::stringstream ss;
888 ss << "Failed to initialize internal transfer file handle";
889 logTransferEvent(LogMask::Error, rec, "OPEN_FAIL",
890 ss.str());
891 return req.SendSimpleResp(rec.status, NULL, NULL, generateClientErr(ss, rec).c_str(), 0);
892 }
893 std::string full_url = prepareURL(req);
894
895 std::string authz = GetAuthz(req);
896
897 int open_results = OpenWaitStall(*fh, full_url, SFS_O_RDONLY, 0644,
898 req.GetSecEntity(), authz);
899 if (SFS_REDIRECT == open_results) {
900 int result = RedirectTransfer(curl, redirect_resource, req, fh->error, rec);
901 return result;
902 } else if (SFS_OK != open_results) {
903 int code;
904 std::stringstream ss;
905 const char *msg = fh->error.getErrText(code);
906 if (msg == NULL) ss << "Failed to open local resource";
907 else ss << msg;
908 rec.status = mapErrNoToHttp(code);
909 logTransferEvent(LogMask::Error, rec, "OPEN_FAIL", msg);
910 int resp_result = req.SendSimpleResp(rec.status, NULL, NULL, generateClientErr(ss, rec).c_str(), 0);
911 fh->close();
912 return resp_result;
913 }
914 ConfigureCurlCA(curl);
915 curl_easy_setopt(curl, CURLOPT_URL, resource.c_str());
916
917 Stream stream(std::move(fh), 0, 0, m_log);
918 State state(0, stream, curl, true, req.tpcForwardCreds);
919 state.SetupHeaders(req);
920
921 return RunCurlWithUpdates(curl, req, state, rec);
922}
923
924/******************************************************************************/
925/* T P C H a n d l e r : : P r o c e s s P u l l R e q */
926/******************************************************************************/
927
928int TPCHandler::ProcessPullReq(const std::string &resource, XrdHttpExtReq &req) {
929 TPCLogRecord rec(req,TpcType::Pull);
930 rec.log_prefix = "PullRequest";
931 rec.local = req.resource;
932 rec.remote = resource;
933 rec.m_log = &m_log;
934 char *name = req.GetSecEntity().name;
935 req.GetClientID(rec.clID);
936 if (name) rec.name = name;
937 logTransferEvent(LogMask::Info, rec, "PULL_START", "Starting a pull request");
938
939 ManagedCurlHandle curlPtr(curl_easy_init());
940 auto curl = curlPtr.get();
941 if (!curl) {
942 std::stringstream ss;
943 ss << "Failed to initialize internal transfer resources";
944 rec.status = 500;
945 logTransferEvent(LogMask::Error, rec, "PULL_FAIL", ss.str());
946 return req.SendSimpleResp(rec.status, NULL, NULL, generateClientErr(ss, rec).c_str(), 0);
947 }
948 // ddavila 2023-01-05:
949 // The following change was required by the Rucio/SENSE project where
950 // multiple IP addresses, each from a different subnet, are assigned to a
951 // single server and routed differently by SENSE.
952 // The above requires the server to utilize the same IP, that was used to
953 // start the TPC, for the resolution of the given TPC instead of
954 // using any of the IPs available.
955 if (m_fixed_route){
956 XrdNetAddr *nP;
957 int numIP = 0;
958 char buff[1024];
959 char * ip;
960
961 // Get the hostname used to contact the server from the http header
962 auto host_header = XrdOucTUtils::caseInsensitiveFind(req.headers,"host");
963 std::string host_used;
964 if (host_header != req.headers.end()) {
965 host_used = host_header->second;
966 }
967
968 // Get the IP addresses associated with the above hostname
969 XrdNetUtils::GetAddrs(host_used.c_str(), &nP, numIP, XrdNetUtils::prefAuto, 0);
970 int ip_size = nP[0].Format(buff, 1024, XrdNetAddrInfo::fmtAddr,XrdNetAddrInfo::noPort);
971 ip = (char *)malloc(ip_size-1);
972
973 // Substring to get only the address, remove brackets and garbage
974 memcpy(ip, buff+1, ip_size-2);
975 ip[ip_size-2]='\0';
976 logTransferEvent(LogMask::Info, rec, "LOCAL IP", ip);
977
978 curl_easy_setopt(curl, CURLOPT_INTERFACE, ip);
979 }
980 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
981 curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, (long) CURL_HTTP_VERSION_1_1);
982// curl_easy_setopt(curl,CURLOPT_SOCKOPTFUNCTION,sockopt_setcloexec_callback);
983 curl_easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, opensocket_callback);
984 curl_easy_setopt(curl, CURLOPT_OPENSOCKETDATA, &rec);
985 curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_callback);
986 curl_easy_setopt(curl, CURLOPT_SOCKOPTDATA , &rec);
987 curl_easy_setopt(curl, CURLOPT_CLOSESOCKETFUNCTION, closesocket_callback);
988 curl_easy_setopt(curl, CURLOPT_CLOSESOCKETDATA, &rec);
989 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, CONNECT_TIMEOUT);
990 std::unique_ptr<XrdSfsFile> fh(m_sfs->newFile(name, m_monid++));
991 if (!fh.get()) {
992 std::stringstream ss;
993 ss << "Failed to initialize internal transfer file handle";
994 rec.status = 500;
995 logTransferEvent(LogMask::Error, rec, "PULL_FAIL", ss.str());
996 return req.SendSimpleResp(rec.status, NULL, NULL, generateClientErr(ss, rec).c_str(), 0);
997 }
998 auto query_header = XrdOucTUtils::caseInsensitiveFind(req.headers,"xrd-http-fullresource");
999 std::string redirect_resource = req.resource;
1000 if (query_header != req.headers.end()) {
1001 redirect_resource = query_header->second;
1002 }
1004 auto overwrite_header = XrdOucTUtils::caseInsensitiveFind(req.headers,"overwrite");
1005 if ((overwrite_header == req.headers.end()) || (overwrite_header->second == "T")) {
1006 if (! usingEC) mode = SFS_O_TRUNC;
1007 }
1008 int streams = 1;
1009 {
1010 auto streams_header = XrdOucTUtils::caseInsensitiveFind(req.headers,"x-number-of-streams");
1011 if (streams_header != req.headers.end()) {
1012 int stream_req = -1;
1013 try {
1014 stream_req = std::stol(streams_header->second);
1015 } catch (...) { // Handled below
1016 }
1017 if (stream_req < 0 || stream_req > 100) {
1018 std::stringstream ss;
1019 ss << "Invalid request for number of streams";
1020 rec.status = 400;
1021 logTransferEvent(LogMask::Info, rec, "INVALID_REQUEST", ss.str());
1022 return req.SendSimpleResp(rec.status, NULL, NULL, generateClientErr(ss, rec).c_str(), 0);
1023 }
1024 streams = stream_req == 0 ? 1 : stream_req;
1025 }
1026 }
1027 rec.streams = streams;
1028 std::string full_url = prepareURL(req);
1029 std::string authz = GetAuthz(req);
1030 curl_easy_setopt(curl, CURLOPT_URL, resource.c_str());
1031 ConfigureCurlCA(curl);
1032 uint64_t sourceFileContentLength = 0;
1033 {
1034 //Get the content-length of the source file and pass it to the OSS layer
1035 //during the open
1036 bool success = false;
1037 bool mismatchDigests = false;
1038 std::map<std::string,std::string> sourceFileReprDigest;
1039 GetRemoteFileInfoTPCPull(curl, req, sourceFileContentLength, sourceFileReprDigest, success, rec);
1040 if(success) {
1041 //In the case we cannot get the information from the source server (offline or other error)
1042 //we just don't add the file information to the opaque of the local file to open
1043 full_url += "&oss.asize=" + std::to_string(sourceFileContentLength);
1044 mismatchDigests = mismatchReprDigest(sourceFileReprDigest,req,rec);
1045 }
1046 if(!success || mismatchDigests) {
1047 // We could not get remote file information, or the checksum provided by the client
1048 // does not match the source file one, we already sent the error to the client so we
1049 // just exit here
1050 return 0;
1051 }
1052 }
1053 int open_result = OpenWaitStall(*fh, full_url, mode|SFS_O_WRONLY,
1054 0644 | SFS_O_MKPTH,
1055 req.GetSecEntity(), authz);
1056 if (SFS_REDIRECT == open_result) {
1057 int result = RedirectTransfer(curl, redirect_resource, req, fh->error, rec);
1058 return result;
1059 } else if (SFS_OK != open_result) {
1060 int code;
1061 std::stringstream ss;
1062 const char *msg = fh->error.getErrText(code);
1063 if ((msg == NULL) || (*msg == '\0')) ss << "Failed to open local resource";
1064 else ss << msg;
1065 rec.status = mapErrNoToHttp(code);
1066 logTransferEvent(LogMask::Error, rec, "OPEN_FAIL", ss.str());
1067 int resp_result = req.SendSimpleResp(rec.status, NULL, NULL,
1068 generateClientErr(ss, rec).c_str(), 0);
1069 fh->close();
1070 return resp_result;
1071 }
1072 Stream stream(std::move(fh), streams * m_pipelining_multiplier, streams > 1 ? m_block_size : m_small_block_size, m_log);
1073 State state(0, stream, curl, false, req.tpcForwardCreds);
1074 state.SetupHeaders(req);
1075 state.SetContentLength(sourceFileContentLength);
1076
1077 if (streams > 1) {
1078 return RunCurlWithStreams(req, state, streams, rec);
1079 } else {
1080 return RunCurlWithUpdates(curl, req, state, rec);
1081 }
1082}
1083
1084/******************************************************************************/
1085/* T P C H a n d l e r : : l o g T r a n s f e r E v e n t */
1086/******************************************************************************/
1087
1088void TPCHandler::logTransferEvent(LogMask mask, const TPCLogRecord &rec,
1089 const std::string &event, const std::string &message)
1090{
1091 if (!(m_log.getMsgMask() & mask)) {return;}
1092
1093 std::stringstream ss;
1094 ss << "event=" << event << ", local=" << rec.local << ", remote=" << rec.remote;
1095 if (rec.name.empty())
1096 ss << ", user=(anonymous)";
1097 else
1098 ss << ", user=" << rec.name;
1099 if (rec.streams != 1)
1100 ss << ", streams=" << rec.streams;
1101 if (rec.bytes_transferred >= 0)
1102 ss << ", bytes_transferred=" << rec.bytes_transferred;
1103 if (rec.status >= 0)
1104 ss << ", status=" << rec.status;
1105 if (rec.tpc_status >= 0)
1106 ss << ", tpc_status=" << rec.tpc_status;
1107 if (!message.empty())
1108 ss << "; " << message;
1109 m_log.Log(mask, rec.log_prefix.c_str(), ss.str().c_str());
1110}
1111
1112std::string TPCHandler::generateClientErr(std::stringstream &err_ss, const TPCLogRecord &rec, CURLcode cCode) {
1113 std::stringstream ssret;
1114 ssret << "failure: " << err_ss.str() << ", local=" << rec.local <<", remote=" << rec.remote;
1115 if(cCode != CURLcode::CURLE_OK) {
1116 ssret << ", HTTP library failure=" << curl_easy_strerror(cCode);
1117 }
1118 return ssret.str();
1119}
1120/******************************************************************************/
1121/* X r d H t t p G e t E x t H a n d l e r */
1122/******************************************************************************/
1123
1124extern "C" {
1125
1126XrdHttpExtHandler *XrdHttpGetExtHandler(XrdSysError *log, const char * config, const char * /*parms*/, XrdOucEnv *myEnv) {
1127 if (curl_global_init(CURL_GLOBAL_DEFAULT)) {
1128 log->Emsg("TPCInitialize", "libcurl failed to initialize");
1129 return NULL;
1130 }
1131
1132 TPCHandler *retval{NULL};
1133 if (!config) {
1134 log->Emsg("TPCInitialize", "TPC handler requires a config filename in order to load");
1135 return NULL;
1136 }
1137 try {
1138 log->Emsg("TPCInitialize", "Will load configuration for the TPC handler from", config);
1139 retval = new TPCHandler(log, config, myEnv);
1140 } catch (std::runtime_error &re) {
1141 log->Emsg("TPCInitialize", "Encountered a runtime failure when loading ", re.what());
1142 //printf("Provided env vars: %p, XrdInet*: %p\n", myEnv, myEnv->GetPtr("XrdInet*"));
1143 }
1144 return retval;
1145}
1146
1147}
void CURL
XrdHttpExtHandler * XrdHttpGetExtHandler(XrdHttpExtHandlerArgs)
static std::string PrepareURL(const std::string &input)
XrdVERSIONINFO(XrdHttpGetExtHandler, HttpTPC)
std::string encode_xrootd_opaque_to_uri(CURL *curl, const std::string &opaque)
int mapErrNoToHttp(int errNo)
Utility functions for XrdHTTP.
std::string encode_str(const std::string &str)
#define close(a)
Definition XrdPosix.hh:48
void getline(uchar *buff, int blen)
#define SFS_REDIRECT
#define SFS_O_MKPTH
#define SFS_STALL
#define SFS_O_RDONLY
#define SFS_STARTED
#define SFS_O_WRONLY
#define SFS_O_CREAT
int XrdSfsFileOpenMode
#define SFS_OK
#define SFS_O_TRUNC
#define AtomicInc(x)
#define AtomicBeg(Mtx)
#define AtomicEnd(Mtx)
if(Avsz)
const std::map< std::string, std::string > & GetReprDigest() const
int GetStatusCode() const
off_t BytesTransferred() const
void SetErrorMessage(const std::string &error_msg)
int GetErrorCode() const
std::string GetErrorMessage() const
std::string GetConnectionDescription()
void SetupHeaders(XrdHttpExtReq &req)
void SetContentLength(const off_t content_length)
off_t GetContentLength() const
void SetErrorCode(int error_code)
void SetupHeadersForHEAD(XrdHttpExtReq &req)
TPCHandler(XrdSysError *log, const char *config, XrdOucEnv *myEnv)
virtual int ProcessReq(XrdHttpExtReq &req)
virtual ~TPCHandler()
virtual bool MatchesPath(const char *verb, const char *path)
Tells if the incoming path is recognized as one of the paths that have to be processed.
std::string clienthost
int ChunkResp(const char *body, long long bodylen)
Send a (potentially partial) body in a chunked response; invoking with NULL body.
void GetClientID(std::string &clid)
std::map< std::string, std::string > & headers
std::string resource
std::map< std::string, std::string > mReprDigest
Repr-Digest map where the key is the digest name and the value is the base64 encoded digest value.
int StartChunkedResp(int code, const char *desc, const char *header_to_add)
Starts a chunked response; body of request is sent over multiple parts using the SendChunkResp.
const XrdSecEntity & GetSecEntity() const
int SendSimpleResp(int code, const char *desc, const char *header_to_add, const char *body, long long bodylen)
Sends a basic response. If the length is < 0 then it is calculated internally.
static std::string prepareOpenURL(PrepareOpenURLParams &params)
static const int noPort
Do not add port number.
int Format(char *bAddr, int bLen, fmtUse fmtType=fmtAuto, int fmtOpts=0)
@ fmtAddr
Address using suitable ipv4 or ipv6 format.
static const char * GetAddrs(const char *hSpec, XrdNetAddr *aListP[], int &aListN, AddrOpts opts=allIPMap, int pNum=PortInSpec)
void * GetPtr(const char *varname)
Definition XrdOucEnv.cc:263
const char * getErrText()
void setUCap(int ucval)
Set user capabilties.
static std::map< std::string, T >::const_iterator caseInsensitiveFind(const std::map< std::string, T > &m, const std::string &lowerCaseSearchKey)
char * name
Entity's name.
XrdOucErrInfo & error
virtual int open(const char *fileName, XrdSfsFileOpenMode openMode, mode_t createMode, const XrdSecEntity *client=0, const char *opaque=0)=0
virtual int close()=0
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
XrdSysLogger * logger(XrdSysLogger *lp=0)
std::unique_ptr< CURL, CurlDeleter > ManagedCurlHandle
void operator()(CURL *curl)
static const int uIPv64
ucap: Supports only IPv4 info