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

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

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

◆ 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

References SFS_ERROR.

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

References State().

Here is the call graph for this function:

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

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: