6#include "XrdVersion.hh"
23 curl_slist_free_all(m_headers);
25 if (m_curl) {curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, m_headers);}
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) {
49 curl_easy_setopt(m_curl, CURLOPT_READDATA,
this);
51 curl_easy_setopt(m_curl, CURLOPT_WRITEDATA,
this);
54 tpcForwardCreds = other.tpcForwardCreds;
55 other.m_headers_copy.clear();
57 other.m_headers = NULL;
58 other.m_stream = NULL;
59 other.m_repr_digests = m_repr_digests;
63bool State::InstallHandlers(
CURL *curl) {
64 curl_easy_setopt(curl, CURLOPT_USERAGENT,
"xrootd-tpc/" XrdVERSION);
65 curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, &State::HeaderCB);
66 curl_easy_setopt(curl, CURLOPT_HEADERDATA,
this);
67 if(m_is_transfer_state) {
69 curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
70 curl_easy_setopt(curl, CURLOPT_READFUNCTION, &State::ReadCB);
71 curl_easy_setopt(curl, CURLOPT_READDATA,
this);
72 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &State::PushRespCB);
73 curl_easy_setopt(curl, CURLOPT_WRITEDATA,
this);
76 m_push_length = buf.st_size;
77 curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, buf.st_size);
80 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &State::WriteCB);
81 curl_easy_setopt(curl, CURLOPT_WRITEDATA,
this);
84 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
86 curl_easy_setopt(curl,CURLOPT_UNRESTRICTED_AUTH,1L);
91 curl_version_info_data *curl_ver = curl_version_info(CURLVERSION_NOW);
92 if (curl_ver->age > 0 && curl_ver->version_num >= 0x072600) {
94 curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 2*60);
95 curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 10*1024);
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);
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());
123 if(m_is_transfer_state && !m_push && !req.
mReprDigest.empty()) {
125 std::stringstream ss;
126 ss <<
"Want-Repr-Digest: ";
131 ss << kv.first <<
'=' << 5;
132 if(cpt < reprDigestSize) {
137 list = curl_slist_append(list, ss.str().c_str());
138 m_headers_copy.emplace_back(ss.str());
141 if (m_is_transfer_state && m_push && m_push_length > 0) {
148 list = curl_slist_append(list,
"Expect: 100-continue");
151 if(reprDigest != req.
headers.end()) {
152 std::string reprDigestHeader {
"Repr-Digest: " + reprDigest->second};
153 curl_slist_append(list,reprDigestHeader.c_str());
157 if (list !=
nullptr) {
158 curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, list);
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());
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());
178 std::stringstream ss;
179 ss <<
"Want-Repr-Digest: ";
184 ss << kv.first <<
'=' << 5;
185 if(cpt < reprDigestSize) {
190 list = curl_slist_append(list, ss.str().c_str());
193 if (list !=
nullptr) {
194 curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, list);
201 m_content_length = -1;
203 m_recv_all_headers =
false;
204 m_recv_status_line =
false;
205 m_repr_digests.clear();
208size_t State::HeaderCB(
char *buffer,
size_t size,
size_t nitems,
void *userdata)
211 std::string header(buffer, size*nitems);
212 return obj->Header(header);
215int State::Header(
const std::string &header) {
217 if (m_recv_all_headers) {
218 m_recv_all_headers =
false;
219 m_recv_status_line =
false;
221 if (!m_recv_status_line) {
222 std::stringstream ss(header);
224 if (!std::getline(ss, item,
' '))
return 0;
225 m_resp_protocol = item;
227 if (!std::getline(ss, item,
' '))
return 0;
229 m_status_code = std::stol(item);
233 m_recv_status_line =
true;
234 }
else if (header.size() == 0 || header ==
"\n" || header ==
"\r\n") {
235 m_recv_all_headers =
true;
237 else if (header !=
"\r\n") {
239 std::size_t found = header.find(
":");
240 if (found != std::string::npos) {
241 std::string header_name = header.substr(0, found);
242 std::transform(header_name.begin(), header_name.end(), header_name.begin(), ::tolower);
243 std::string header_value = header.substr(found+1);
244 if (header_name ==
"content-length")
247 m_content_length = std::stoll(header_value);
254 if(header_name ==
"repr-digest") {
264 return header.size();
267size_t State::WriteCB(
void *buffer,
size_t size,
size_t nitems,
void *userdata) {
273 obj->m_error_buf += std::string(
static_cast<char*
>(buffer),
274 std::min(
static_cast<size_t>(1024), size*nitems));
276 if (obj->m_error_buf.size() >= 1024)
281 return obj->Write(
static_cast<char*
>(buffer), size*nitems);
289size_t State::PushRespCB(
void *buffer,
size_t size,
size_t nitems,
void *userdata) {
296 obj->m_error_buf += std::string(
static_cast<char*
>(buffer),
297 std::min(
static_cast<size_t>(1024), size*nitems));
299 if (obj->m_error_buf.size() >= 1024)
307ssize_t State::Write(
char *buffer,
size_t size) {
308 ssize_t retval = m_stream->Write(m_start_offset + m_offset, buffer, size,
false);
310 m_error_buf = m_stream->GetErrorMessage();
323 ssize_t retval = m_stream->Write(m_start_offset + m_offset, 0, 0,
true);
325 m_error_buf = m_stream->GetErrorMessage();
333size_t State::ReadCB(
void *buffer,
size_t size,
size_t nitems,
void *userdata) {
337 return obj->Read(
static_cast<char*
>(buffer), size*nitems);
340int State::Read(
char *buffer,
size_t size) {
341 int retval = m_stream->Read(m_start_offset + m_offset, buffer, size);
351 CURL *curl = curl_easy_duphandle(m_curl);
353 throw std::runtime_error(
"Failed to duplicate existing curl handle.");
356 State *state =
new State(0, *m_stream, curl, m_push, tpcForwardCreds);
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();
363 state->m_headers = curl_slist_append(state->m_headers, header_iter->c_str());
364 state->m_headers_copy.push_back(*header_iter);
366 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, NULL);
367 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, state->m_headers);
374 m_start_offset = offset;
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());
384 return m_stream->AvailableBuffers();
389 m_stream->DumpBuffers();
394 if (!m_stream->Finalize()) {
395 m_error_buf = m_stream->GetErrorMessage();
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) {
413 rc = curl_easy_getinfo(m_curl, CURLINFO_PRIMARY_PORT, &curl_port);
414 if ((rc != CURLE_OK) || !curl_port) {
417 std::stringstream ss;
423 if (NULL == strchr(curl_ip,
':'))
424 ss <<
"tcp:" << curl_ip <<
":" << curl_port;
426 ss <<
"tcp:[" << curl_ip <<
"]:" << curl_port;
int GetStatusCode() const
void SetTransferParameters(off_t offset, size_t size)
std::string GetConnectionDescription()
void SetupHeaders(XrdHttpExtReq &req)
void SetupHeadersForHEAD(XrdHttpExtReq &req)
int AvailableBuffers() const
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)