XRootD
Loading...
Searching...
No Matches
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(), and Move().

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}

◆ 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}

◆ 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}

References errClose.

◆ 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

References errFlush, and SFS_ERROR.

◆ 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}

References State().

Here is the call graph for this function:

◆ 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)

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: