XRootD
TPC::State Class Reference

#include <XrdHttpTpcState.hh>

+ Collaboration diagram for TPC::State:

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
 
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.

Constructor & Destructor Documentation

◆ State() [1/3]

TPC::State::State ( )
inline

Definition at line 24 of file XrdHttpTpcState.hh.

24  :
25  m_push(true),
26  m_recv_status_line(false),
27  m_recv_all_headers(false),
28  m_offset(0),
29  m_start_offset(0),
30  m_status_code(-1),
31  m_error_code(0),
32  m_content_length(-1),
33  m_stream(NULL),
34  m_curl(NULL),
35  m_headers(NULL),
36  m_is_transfer_state(true)
37  {}

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 45 of file XrdHttpTpcState.hh.

45  :
46  m_push(true),
47  m_recv_status_line(false),
48  m_recv_all_headers(false),
49  m_offset(0),
50  m_start_offset(0),
51  m_status_code(-1),
52  m_error_code(0),
53  m_content_length(-1),
54  m_push_length(-1),
55  m_stream(NULL),
56  m_curl(curl),
57  m_headers(NULL),
58  m_is_transfer_state(false),
59  tpcForwardCreds(tpcForwardCreds)
60  {
61  InstallHandlers(curl);
62  }

◆ State() [3/3]

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

Definition at line 67 of file XrdHttpTpcState.hh.

67  :
68  m_push(push),
69  m_recv_status_line(false),
70  m_recv_all_headers(false),
71  m_offset(0),
72  m_start_offset(start_offset),
73  m_status_code(-1),
74  m_error_code(0),
75  m_content_length(-1),
76  m_push_length(-1),
77  m_stream(&stream),
78  m_curl(curl),
79  m_headers(NULL),
80  m_is_transfer_state(true),
81  tpcForwardCreds(tpcForwardCreds)
82  {
83  InstallHandlers(curl);
84  }

◆ ~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 382 of file XrdHttpTpcState.cc.

383 {
384  return m_stream->AvailableBuffers();
385 }
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 122 of file XrdHttpTpcState.hh.

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

◆ BytesTransferred()

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

Definition at line 94 of file XrdHttpTpcState.hh.

94 {return m_offset;}

◆ DumpBuffers()

void State::DumpBuffers ( ) const

Definition at line 387 of file XrdHttpTpcState.cc.

388 {
389  m_stream->DumpBuffers();
390 }
void DumpBuffers() const

References TPC::Stream::DumpBuffers().

+ Here is the call graph for this function:

◆ Duplicate()

State * State::Duplicate ( )

Definition at line 350 of file XrdHttpTpcState.cc.

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

References State().

+ Here is the call graph for this function:

◆ Finalize()

bool State::Finalize ( )

Definition at line 392 of file XrdHttpTpcState.cc.

393 {
394  if (!m_stream->Finalize()) {
395  m_error_buf = m_stream->GetErrorMessage();
396  m_error_code = 3;
397  return false;
398  }
399  return true;
400 }
std::string GetErrorMessage() const

References 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  ssize_t retval = m_stream->Write(m_start_offset + m_offset, 0, 0, true);
324  if (retval == SFS_ERROR) {
325  m_error_buf = m_stream->GetErrorMessage();
326  m_error_code = 2;
327  return -1;
328  }
329  m_offset += retval;
330  return retval;
331 }
#define SFS_ERROR
ssize_t Write(off_t offset, const char *buffer, size_t size, bool force)

References TPC::Stream::GetErrorMessage(), SFS_ERROR, and TPC::Stream::Write().

+ Here is the call graph for this function:

◆ GetConnectionDescription()

std::string State::GetConnectionDescription ( )

Definition at line 402 of file XrdHttpTpcState.cc.

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

◆ GetContentLength()

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

Definition at line 98 of file XrdHttpTpcState.hh.

98 {return m_content_length;}

◆ GetErrorCode()

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

Definition at line 102 of file XrdHttpTpcState.hh.

102 {return m_error_code;}

◆ GetErrorMessage()

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

Definition at line 108 of file XrdHttpTpcState.hh.

108 {return m_error_buf;}

◆ GetHandle()

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

Definition at line 114 of file XrdHttpTpcState.hh.

114 {return m_curl;}

◆ GetReprDigest()

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

Definition at line 100 of file XrdHttpTpcState.hh.

100 { return m_repr_digests; }

◆ GetStatusCode()

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

Definition at line 106 of file XrdHttpTpcState.hh.

106 {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 198 of file XrdHttpTpcState.cc.

198  {
199  m_offset = 0;
200  m_status_code = -1;
201  m_content_length = -1;
202  m_push_length = -1;
203  m_recv_all_headers = false;
204  m_recv_status_line = false;
205  m_repr_digests.clear();
206 }

◆ SetContentLength()

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

Definition at line 96 of file XrdHttpTpcState.hh.

96 { m_content_length = content_length; }

◆ SetErrorCode()

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

Definition at line 104 of file XrdHttpTpcState.hh.

104 {m_error_code = error_code;}

◆ SetErrorMessage()

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

Definition at line 110 of file XrdHttpTpcState.hh.

110 {m_error_buf = error_msg;}

◆ SetTransferParameters()

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

Definition at line 373 of file XrdHttpTpcState.cc.

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

◆ 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 107 of file XrdHttpTpcState.cc.

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

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

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


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