XRootD
TPC::State Class Reference

#include <XrdHttpTpcState.hh>

+ Collaboration diagram for TPC::State:

Public Types

enum  ErrorCode {
  errNone = 0 ,
  errWrite = 1 ,
  errFlush = 2 ,
  errClose = 3 ,
  errTimeout = 10
}
 

Public Member Functions

 State ()
 
 State (CURL *curl, bool tpcForwardCreds)
 
 State (off_t start_offset, Stream &stream, CURL *curl, bool push, bool tpcForwardCreds)
 
 ~State ()
 
int AvailableBuffers () const
 
bool BodyTransferInProgress () const
 
off_t BytesTransferred () const
 
void DumpBuffers () const
 
StateDuplicate ()
 
bool Finalize ()
 
int Flush ()
 
std::string GetConnectionDescription ()
 
off_t GetContentLength () const
 
int GetErrorCode () const
 
std::string GetErrorMessage () const
 
int GetFinalizeErrorCode () const
 
std::string GetFinalizeErrorMessage () const
 
CURLGetHandle () const
 
const std::map< std::string, std::string > & GetReprDigest () const
 
int GetStatusCode () const
 
void Move (State &other)
 
void ResetAfterRequest ()
 
void SetContentLength (const off_t content_length)
 
void SetErrorCode (int error_code)
 
void SetErrorMessage (const std::string &error_msg)
 
void SetTransferParameters (off_t offset, size_t size)
 
void SetupHeaders (XrdHttpExtReq &req)
 
void SetupHeadersForHEAD (XrdHttpExtReq &req)
 

Detailed Description

Definition at line 21 of file XrdHttpTpcState.hh.

Member Enumeration Documentation

◆ ErrorCode

Enumerator
errNone 
errWrite 
errFlush 
errClose 
errTimeout 

Definition at line 27 of file XrdHttpTpcState.hh.

27  {
28  errNone = 0,
29  errWrite = 1, // Failure while writing the received data to the local file.
30  errFlush = 2, // Failure while flushing the local file.
31  errClose = 3, // Failure while closing the local file.
32  errTimeout = 10 // The transfer did not make any progress within the timeout.
33  };

Constructor & Destructor Documentation

◆ State() [1/3]

TPC::State::State ( )
inline

Definition at line 35 of file XrdHttpTpcState.hh.

35  :
36  m_push(true),
37  m_recv_status_line(false),
38  m_recv_all_headers(false),
39  m_offset(0),
40  m_start_offset(0),
41  m_status_code(-1),
42  m_error_code(0),
43  m_content_length(-1),
44  m_stream(NULL),
45  m_curl(NULL),
46  m_headers(NULL),
47  m_is_transfer_state(true)
48  {}

Referenced by Duplicate().

+ Here is the caller graph for this function:

◆ State() [2/3]

TPC::State::State ( CURL curl,
bool  tpcForwardCreds 
)
inline

Don't use that constructor if you want to do some transfers.

Parameters
curlthe curl handle
tpcForwardCredsset to true if the credentials needs to be forwarded for this request, false otherwise
pushset to true if this HEAD request is for a push transfer, false otherwise

Definition at line 56 of file XrdHttpTpcState.hh.

56  :
57  m_push(true),
58  m_recv_status_line(false),
59  m_recv_all_headers(false),
60  m_offset(0),
61  m_start_offset(0),
62  m_status_code(-1),
63  m_error_code(0),
64  m_content_length(-1),
65  m_push_length(-1),
66  m_stream(NULL),
67  m_curl(curl),
68  m_headers(NULL),
69  m_is_transfer_state(false),
70  tpcForwardCreds(tpcForwardCreds)
71  {
72  InstallHandlers(curl);
73  }

◆ State() [3/3]

TPC::State::State ( off_t  start_offset,
Stream stream,
CURL curl,
bool  push,
bool  tpcForwardCreds 
)
inline

Definition at line 78 of file XrdHttpTpcState.hh.

78  :
79  m_push(push),
80  m_recv_status_line(false),
81  m_recv_all_headers(false),
82  m_offset(0),
83  m_start_offset(start_offset),
84  m_status_code(-1),
85  m_error_code(0),
86  m_content_length(-1),
87  m_push_length(-1),
88  m_stream(&stream),
89  m_curl(curl),
90  m_headers(NULL),
91  m_is_transfer_state(true),
92  tpcForwardCreds(tpcForwardCreds)
93  {
94  InstallHandlers(curl);
95  }

◆ ~State()

State::~State ( )

Definition at line 21 of file XrdHttpTpcState.cc.

21  {
22  if (m_headers) {
23  curl_slist_free_all(m_headers);
24  m_headers = NULL;
25  if (m_curl) {curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, m_headers);}
26  }
27 }

Member Function Documentation

◆ AvailableBuffers()

int State::AvailableBuffers ( ) const

Definition at line 379 of file XrdHttpTpcState.cc.

380 {
381  return m_stream->AvailableBuffers();
382 }
size_t AvailableBuffers() const

References TPC::Stream::AvailableBuffers().

+ Here is the call graph for this function:

◆ BodyTransferInProgress()

bool TPC::State::BodyTransferInProgress ( ) const
inline

Definition at line 142 of file XrdHttpTpcState.hh.

142 {return m_offset && (m_offset != m_content_length);}

◆ BytesTransferred()

off_t TPC::State::BytesTransferred ( ) const
inline

Definition at line 105 of file XrdHttpTpcState.hh.

105 {return m_offset;}

◆ DumpBuffers()

void State::DumpBuffers ( ) const

Definition at line 384 of file XrdHttpTpcState.cc.

385 {
386  m_stream->DumpBuffers();
387 }
void DumpBuffers() const

References TPC::Stream::DumpBuffers().

+ Here is the call graph for this function:

◆ Duplicate()

State * State::Duplicate ( )

Definition at line 347 of file XrdHttpTpcState.cc.

347  {
348  CURL *curl = curl_easy_duphandle(m_curl);
349  if (!curl) {
350  throw std::runtime_error("Failed to duplicate existing curl handle.");
351  }
352 
353  State *state = new State(0, *m_stream, curl, m_push, tpcForwardCreds);
354 
355  if (m_headers) {
356  state->m_headers_copy.reserve(m_headers_copy.size());
357  for (std::vector<std::string>::const_iterator header_iter = m_headers_copy.begin();
358  header_iter != m_headers_copy.end();
359  header_iter++) {
360  state->m_headers = curl_slist_append(state->m_headers, header_iter->c_str());
361  state->m_headers_copy.push_back(*header_iter);
362  }
363  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, NULL);
364  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, state->m_headers);
365  }
366 
367  return state;
368 }
void CURL

References State().

+ Here is the call graph for this function:

◆ Finalize()

bool State::Finalize ( )

Definition at line 389 of file XrdHttpTpcState.cc.

390 {
391  if (!m_stream->Finalize()) {
392  RecordFinalizeError(errClose, m_stream->GetErrorMessage());
393  return false;
394  }
395  return true;
396 }
std::string GetErrorMessage() const

References errClose, TPC::Stream::Finalize(), and TPC::Stream::GetErrorMessage().

+ Here is the call graph for this function:

◆ Flush()

int State::Flush ( )

Definition at line 318 of file XrdHttpTpcState.cc.

318  {
319  if (m_push) {
320  return 0;
321  }
322 
323  if (m_stream->Flush() == SFS_ERROR) {
324  RecordFinalizeError(errFlush, m_stream->GetErrorMessage());
325  return -1;
326  }
327  return 0;
328 }
#define SFS_ERROR
ssize_t Flush()

References errFlush, TPC::Stream::Flush(), TPC::Stream::GetErrorMessage(), and SFS_ERROR.

+ Here is the call graph for this function:

◆ GetConnectionDescription()

std::string State::GetConnectionDescription ( )

Definition at line 398 of file XrdHttpTpcState.cc.

399 {
400  // CURLINFO_PRIMARY_PORT is only defined for 7.21.0 or later; on older
401  // library versions, simply omit this information.
402 #if LIBCURL_VERSION_NUM >= 0x071500
403  char *curl_ip = NULL;
404  CURLcode rc = curl_easy_getinfo(m_curl, CURLINFO_PRIMARY_IP, &curl_ip);
405  if ((rc != CURLE_OK) || !curl_ip) {
406  return "";
407  }
408  long curl_port = 0;
409  rc = curl_easy_getinfo(m_curl, CURLINFO_PRIMARY_PORT, &curl_port);
410  if ((rc != CURLE_OK) || !curl_port) {
411  return "";
412  }
413  std::stringstream ss;
414  // libcurl returns IPv6 addresses of the form:
415  // 2600:900:6:1301:5054:ff:fe0b:9cba:8000
416  // However the HTTP-TPC spec says to use the form
417  // [2600:900:6:1301:5054:ff:fe0b:9cba]:8000
418  // Hence, we add '[' and ']' whenever a ':' is seen.
419  if (NULL == strchr(curl_ip, ':'))
420  ss << "tcp:" << curl_ip << ":" << curl_port;
421  else
422  ss << "tcp:[" << curl_ip << "]:" << curl_port;
423  return ss.str();
424 #else
425  return "";
426 #endif
427 }

◆ GetContentLength()

off_t TPC::State::GetContentLength ( ) const
inline

Definition at line 109 of file XrdHttpTpcState.hh.

109 {return m_content_length;}

◆ GetErrorCode()

int TPC::State::GetErrorCode ( ) const
inline

Definition at line 113 of file XrdHttpTpcState.hh.

113 {return m_error_code;}

◆ GetErrorMessage()

std::string TPC::State::GetErrorMessage ( ) const
inline

Definition at line 119 of file XrdHttpTpcState.hh.

119 {return m_error_buf;}

◆ GetFinalizeErrorCode()

int TPC::State::GetFinalizeErrorCode ( ) const
inline

Definition at line 128 of file XrdHttpTpcState.hh.

128 {return m_finalize_error_code;}

◆ GetFinalizeErrorMessage()

std::string TPC::State::GetFinalizeErrorMessage ( ) const
inline

Definition at line 130 of file XrdHttpTpcState.hh.

130 {return m_finalize_error_buf;}

◆ GetHandle()

CURL* TPC::State::GetHandle ( ) const
inline

Definition at line 134 of file XrdHttpTpcState.hh.

134 {return m_curl;}

◆ GetReprDigest()

const std::map<std::string, std::string>& TPC::State::GetReprDigest ( ) const
inline

Definition at line 111 of file XrdHttpTpcState.hh.

111 { return m_repr_digests; }

◆ GetStatusCode()

int TPC::State::GetStatusCode ( ) const
inline

Definition at line 117 of file XrdHttpTpcState.hh.

117 {return m_status_code;}

◆ Move()

void State::Move ( State other)

Definition at line 30 of file XrdHttpTpcState.cc.

31 {
32  m_push = other.m_push;
33  m_recv_status_line = other.m_recv_status_line;
34  m_recv_all_headers = other.m_recv_all_headers;
35  m_offset = other.m_offset;
36  m_start_offset = other.m_start_offset;
37  m_status_code = other.m_status_code;
38  m_content_length = other.m_content_length;
39  m_push_length = other.m_push_length;
40  m_stream = other.m_stream;
41  m_curl = other.m_curl;
42  m_headers = other.m_headers;
43  m_headers_copy = other.m_headers_copy;
44  m_resp_protocol = other.m_resp_protocol;
45  m_is_transfer_state = other.m_is_transfer_state;
46  curl_easy_setopt(m_curl, CURLOPT_HEADERDATA, this);
47  if (m_is_transfer_state) {
48  if (m_push) {
49  curl_easy_setopt(m_curl, CURLOPT_READDATA, this);
50  } else {
51  curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, this);
52  }
53  }
54  tpcForwardCreds = other.tpcForwardCreds;
55  other.m_headers_copy.clear();
56  other.m_curl = NULL;
57  other.m_headers = NULL;
58  other.m_stream = NULL;
59  other.m_repr_digests = m_repr_digests;
60 }

◆ ResetAfterRequest()

void State::ResetAfterRequest ( )

Definition at line 190 of file XrdHttpTpcState.cc.

190  {
191  m_offset = 0;
192  m_status_code = -1;
193  m_content_length = -1;
194  m_push_length = -1;
195  m_recv_all_headers = false;
196  m_recv_status_line = false;
197  m_repr_digests.clear();
198 }

◆ SetContentLength()

void TPC::State::SetContentLength ( const off_t  content_length)
inline

Definition at line 107 of file XrdHttpTpcState.hh.

107 { m_content_length = content_length; }

◆ SetErrorCode()

void TPC::State::SetErrorCode ( int  error_code)
inline

Definition at line 115 of file XrdHttpTpcState.hh.

115 {m_error_code = error_code;}

◆ SetErrorMessage()

void TPC::State::SetErrorMessage ( const std::string &  error_msg)
inline

Definition at line 121 of file XrdHttpTpcState.hh.

121 {m_error_buf = error_msg;}

◆ SetTransferParameters()

void State::SetTransferParameters ( off_t  offset,
size_t  size 
)

Definition at line 370 of file XrdHttpTpcState.cc.

370  {
371  m_start_offset = offset;
372  m_offset = 0;
373  m_content_length = size;
374  std::stringstream ss;
375  ss << offset << "-" << (offset+size-1);
376  curl_easy_setopt(m_curl, CURLOPT_RANGE, ss.str().c_str());
377 }

◆ SetupHeaders()

void State::SetupHeaders ( XrdHttpExtReq req)

Setup any headers necessary for the GET/PUT operation

Currently includes:

  • Handle the 'Copy-Headers' feature
  • Adding Expect: 100-continue to get around a libcurl bug on uploads.

Definition at line 99 of file XrdHttpTpcState.cc.

99  {
100  struct curl_slist *list = NULL;
101  for (const auto & [header,value]: req.headers) {
102  if (!strncasecmp(header.c_str(),"copy-header", 11)) {
103  list = curl_slist_append(list, value.c_str());
104  m_headers_copy.emplace_back(value);
105  }
106  // Note: len("TransferHeader") == 14
107  if (!strncasecmp(header.c_str(),"transferheader",14)) {
108  std::stringstream ss;
109  ss << header.substr(14) << ": " << value;
110  list = curl_slist_append(list, ss.str().c_str());
111  m_headers_copy.emplace_back(ss.str());
112  }
113  }
114 
115  if(m_is_transfer_state && !m_push && !req.mReprDigest.empty()) {
116  size_t reprDigestSize = req.mReprDigest.size();
117  std::stringstream ss;
118  ss << "Want-Repr-Digest: ";
119  size_t cpt = 1;
120  for (const auto &kv: req.mReprDigest) {
121  // We put the same weight for the digest names as we do not have any way, according to the specs,
122  // to give priority to a digest name in particular
123  ss << kv.first << '=' << 5;
124  if(cpt < reprDigestSize) {
125  ss << ',';
126  }
127  cpt++;
128  }
129  list = curl_slist_append(list, ss.str().c_str());
130  m_headers_copy.emplace_back(ss.str());
131  }
132 
133  if (m_is_transfer_state && m_push && m_push_length > 0) {
134  // On libcurl 8.5.0 - 8.9.1, we've observed bugs causing failures whenever
135  // `Expect: 100-continue` is not used. Older versions of libcurl unconditionally
136  // set `Expect` whenever PUT is used (likely an older bug). To workaround the issue,
137  // we force `Expect` to be set, triggering the older libcurl behavior.
138  // See: https://github.com/xrootd/xrootd/issues/2470
139  // See: https://github.com/curl/curl/issues/17004
140  list = curl_slist_append(list, "Expect: 100-continue");
141  // Add Repr-Digest header to PUT request (PUSH)
142  auto reprDigest = XrdOucTUtils::caseInsensitiveFind(req.headers,"repr-digest");
143  if(reprDigest != req.headers.end()) {
144  std::string reprDigestHeader {"Repr-Digest: " + reprDigest->second};
145  curl_slist_append(list,reprDigestHeader.c_str());
146  }
147  }
148 
149  if (list != nullptr) {
150  curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, list);
151  m_headers = list;
152  }
153 }
std::map< std::string, std::string > & headers
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.
static std::map< std::string, T >::const_iterator caseInsensitiveFind(const std::map< std::string, T > &m, const std::string &lowerCaseSearchKey)
Definition: XrdOucTUtils.hh:79

References XrdOucTUtils::caseInsensitiveFind(), XrdHttpExtReq::headers, and XrdHttpExtReq::mReprDigest.

+ Here is the call graph for this function:

◆ SetupHeadersForHEAD()

void State::SetupHeadersForHEAD ( XrdHttpExtReq req)

Definition at line 155 of file XrdHttpTpcState.cc.

155  {
156  struct curl_slist *list = NULL;
157  for (const auto & [header,value]: req.headers) {
158  if (!strncasecmp(header.c_str(),"copy-header", 11)) {
159  list = curl_slist_append(list, value.c_str());
160  }
161  // Note: len("TransferHeader") == 14
162  if (!strncasecmp(header.c_str(),"transferheader",14)) {
163  std::stringstream ss;
164  ss << header.substr(14) << ": " << value;
165  list = curl_slist_append(list, ss.str().c_str());
166  }
167  }
168  if(!req.mReprDigest.empty()) {
169  size_t reprDigestSize = req.mReprDigest.size();
170  std::stringstream ss;
171  ss << "Want-Repr-Digest: ";
172  size_t cpt = 1;
173  for (const auto &kv: req.mReprDigest) {
174  // We put the same weight for the digest names as we do not have any way, according to the specs,
175  // to give priority to a digest name in particular
176  ss << kv.first << '=' << 5;
177  if(cpt < reprDigestSize) {
178  ss << ',';
179  }
180  cpt++;
181  }
182  list = curl_slist_append(list, ss.str().c_str());
183  }
184 
185  if (list != nullptr) {
186  curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, list);
187  }
188 }

References XrdHttpExtReq::headers, and XrdHttpExtReq::mReprDigest.


The documentation for this class was generated from the following files: