XRootD
XrdClHttp::HeaderParser Class Reference

#include <XrdClHttpUtil.hh>

+ Collaboration diagram for XrdClHttp::HeaderParser:

Public Member Functions

 HeaderParser ()
 
VerbsCache::HttpVerbs GetAllowedVerbs () const
 
const std::string & GetCacheControl () const
 
const XrdClHttp::ChecksumInfoGetChecksums () const
 
int64_t GetContentLength () const
 
const std::string & GetETag () const
 
const std::string & GetLocation () const
 
uint64_t GetOffset () const
 
int GetStatusCode () const
 
std::string GetStatusMessage () const
 
bool HeadersDone () const
 
bool IsMultipartByterange () const
 
ResponseInfo::HeaderMap && MoveHeaders ()
 
const std::string & MultipartSeparator () const
 
bool Parse (const std::string &headers)
 
void SetMultipartSeparator (const std::string_view &sep)
 
void SetStatusCode (int sc)
 

Static Public Member Functions

static bool Base64Decode (std::string_view input, std::array< unsigned char, 32 > &output)
 
static bool Canonicalize (std::string &headerName)
 
static std::string ChecksumTypeToDigestName (XrdClHttp::ChecksumType type)
 
static void ParseDigest (const std::string &digest, XrdClHttp::ChecksumInfo &info)
 

Detailed Description

Definition at line 71 of file XrdClHttpUtil.hh.

Constructor & Destructor Documentation

◆ HeaderParser()

XrdClHttp::HeaderParser::HeaderParser ( )
inline

Definition at line 73 of file XrdClHttpUtil.hh.

73 {}

Member Function Documentation

◆ Base64Decode()

bool HeaderParser::Base64Decode ( std::string_view  input,
std::array< unsigned char, 32 > &  output 
)
static

Definition at line 246 of file XrdClHttpUtil.cc.

246  {
247  if (input.size() > 44 || input.size() % 4 != 0) return false;
248  if (input.size() == 0) return true;
249 
250  std::unique_ptr<BIO, decltype(&BIO_free_all)> b64(BIO_new(BIO_f_base64()), &BIO_free_all);
251  BIO_set_flags(b64.get(), BIO_FLAGS_BASE64_NO_NL);
252  std::unique_ptr<BIO, decltype(&BIO_free_all)> bmem(
253  BIO_new_mem_buf(const_cast<char *>(input.data()), input.size()), &BIO_free_all);
254  bmem.reset(BIO_push(b64.release(), bmem.release()));
255 
256  // Compute expected length of output; used to verify BIO_read consumes all input
257  size_t expectedLen = static_cast<size_t>(input.size() * 0.75);
258  if (input[input.size() - 1] == '=') {
259  expectedLen -= 1;
260  if (input[input.size() - 2] == '=') {
261  expectedLen -= 1;
262  }
263  }
264 
265  auto len = BIO_read(bmem.get(), &output[0], output.size());
266 
267  if (len == -1 || static_cast<size_t>(len) != expectedLen) return false;
268 
269  return true;
270 }

Referenced by ParseDigest().

+ Here is the caller graph for this function:

◆ Canonicalize()

bool HeaderParser::Canonicalize ( std::string &  headerName)
static

Definition at line 539 of file XrdClHttpUtil.cc.

540 {
541  auto upper = true;
542  const static int toLower = 'a' - 'A';
543  for (size_t idx=0; idx<headerName.size(); idx++) {
544  char c = headerName[idx];
545  if (!validHeaderByte(c)) {
546  return false;
547  }
548  if (upper && 'a' <= c && c <= 'z') {
549  c -= toLower;
550  } else if (!upper && 'A' <= c && c <= 'Z') {
551  c += toLower;
552  }
553  headerName[idx] = c;
554  upper = c == '-';
555  }
556  return true;
557 }

Referenced by Parse().

+ Here is the caller graph for this function:

◆ ChecksumTypeToDigestName()

std::string HeaderParser::ChecksumTypeToDigestName ( XrdClHttp::ChecksumType  type)
static

Definition at line 494 of file XrdClHttpUtil.cc.

494  {
495  switch (type) {
496  case XrdClHttp::ChecksumType::kMD5:
497  return "MD5";
498  case XrdClHttp::ChecksumType::kCRC32C:
499  return "CRC32c";
500  case XrdClHttp::ChecksumType::kSHA1:
501  return "SHA";
502  case XrdClHttp::ChecksumType::kSHA256:
503  return "SHA-256";
504  default:
505  return "";
506  }
507 }

Referenced by XrdClHttp::CurlChecksumOp::Setup().

+ Here is the caller graph for this function:

◆ GetAllowedVerbs()

VerbsCache::HttpVerbs XrdClHttp::HeaderParser::GetAllowedVerbs ( ) const
inline

Definition at line 110 of file XrdClHttpUtil.hh.

111  {
112  return VerbsCache::HttpVerbs(m_allow_verbs);
113  }

Referenced by XrdClHttp::CurlOptionsOp::Success().

+ Here is the caller graph for this function:

◆ GetCacheControl()

const std::string& XrdClHttp::HeaderParser::GetCacheControl ( ) const
inline

Definition at line 119 of file XrdClHttpUtil.hh.

119 {return m_cache_control;}

Referenced by XrdClHttp::CurlPrefetchOpenOp::Pause().

+ Here is the caller graph for this function:

◆ GetChecksums()

const XrdClHttp::ChecksumInfo& XrdClHttp::HeaderParser::GetChecksums ( ) const
inline

Definition at line 122 of file XrdClHttpUtil.hh.

122 {return m_checksums;}

Referenced by XrdClHttp::CurlChecksumOp::Success().

+ Here is the caller graph for this function:

◆ GetContentLength()

int64_t XrdClHttp::HeaderParser::GetContentLength ( ) const
inline

Definition at line 77 of file XrdClHttpUtil.hh.

77 {return m_content_length;}

Referenced by XrdClHttp::CurlStatOp::GetStatInfo(), and XrdClHttp::CurlPrefetchOpenOp::Pause().

+ Here is the caller graph for this function:

◆ GetETag()

const std::string& XrdClHttp::HeaderParser::GetETag ( ) const
inline

Definition at line 118 of file XrdClHttpUtil.hh.

118 {return m_etag;}

Referenced by XrdClHttp::CurlPrefetchOpenOp::Pause(), and XrdClHttp::CurlQueryOp::Success().

+ Here is the caller graph for this function:

◆ GetLocation()

const std::string& XrdClHttp::HeaderParser::GetLocation ( ) const
inline

Definition at line 117 of file XrdClHttpUtil.hh.

117 {return m_location;}

Referenced by XrdClHttp::CurlStatOp::OptionsDone().

+ Here is the caller graph for this function:

◆ GetOffset()

uint64_t XrdClHttp::HeaderParser::GetOffset ( ) const
inline

Definition at line 79 of file XrdClHttpUtil.hh.

79 {return m_response_offset;}

Referenced by XrdClHttp::CurlVectorReadOp::Write().

+ Here is the caller graph for this function:

◆ GetStatusCode()

int XrdClHttp::HeaderParser::GetStatusCode ( ) const
inline

Definition at line 90 of file XrdClHttpUtil.hh.

90 {return m_status_code;}

Referenced by XrdClHttp::CurlOperation::GetStatusCode(), and XrdClHttp::CurlOperation::IsRedirect().

+ Here is the caller graph for this function:

◆ GetStatusMessage()

std::string XrdClHttp::HeaderParser::GetStatusMessage ( ) const
inline

Definition at line 115 of file XrdClHttpUtil.hh.

115 {return m_resp_message;}

Referenced by XrdClHttp::CurlOperation::GetStatusMessage().

+ Here is the caller graph for this function:

◆ HeadersDone()

bool XrdClHttp::HeaderParser::HeadersDone ( ) const
inline

Definition at line 83 of file XrdClHttpUtil.hh.

83 {return m_recv_all_headers;}

◆ IsMultipartByterange()

bool XrdClHttp::HeaderParser::IsMultipartByterange ( ) const
inline

Definition at line 97 of file XrdClHttpUtil.hh.

97 {return m_multipart_byteranges;}

Referenced by XrdClHttp::CurlVectorReadOp::Write().

+ Here is the caller graph for this function:

◆ MoveHeaders()

ResponseInfo::HeaderMap&& XrdClHttp::HeaderParser::MoveHeaders ( )
inline

Definition at line 88 of file XrdClHttpUtil.hh.

88 {return std::move(m_headers);}

◆ MultipartSeparator()

const std::string& XrdClHttp::HeaderParser::MultipartSeparator ( ) const
inline

Definition at line 101 of file XrdClHttpUtil.hh.

101 {return m_multipart_sep;}

Referenced by XrdClHttp::CurlVectorReadOp::Write().

+ Here is the caller graph for this function:

◆ Parse()

bool HeaderParser::Parse ( const std::string &  headers)

Definition at line 277 of file XrdClHttpUtil.cc.

278 {
279  if (m_recv_all_headers) {
280  m_recv_all_headers = false;
281  m_recv_status_line = false;
282  }
283 
284  if (!m_recv_status_line) {
285  m_recv_status_line = true;
286 
287  std::stringstream ss(header_line);
288  std::string item;
289  if (!std::getline(ss, item, ' ')) return false;
290  m_resp_protocol = item;
291  if (!std::getline(ss, item, ' ')) return false;
292  try {
293  m_status_code = std::stol(item);
294  } catch (...) {
295  return false;
296  }
297  if (m_status_code < 100 || m_status_code >= 600) {
298  return false;
299  }
300  if (!std::getline(ss, item, '\n')) return false;
301  auto cr_loc = item.find('\r');
302  if (cr_loc != std::string::npos) {
303  m_resp_message = item.substr(0, cr_loc);
304  } else {
305  m_resp_message = item;
306  }
307  return true;
308  }
309 
310  if (header_line.empty() || header_line == "\n" || header_line == "\r\n") {
311  m_recv_all_headers = true;
312  return true;
313  }
314 
315  auto found = header_line.find(":");
316  if (found == std::string::npos) {
317  return false;
318  }
319 
320  std::string header_name = header_line.substr(0, found);
321  if (!Canonicalize(header_name)) {
322  return false;
323  }
324 
325  found += 1;
326  while (found < header_line.size()) {
327  if (header_line[found] != ' ') {break;}
328  found += 1;
329  }
330  std::string header_value = header_line.substr(found);
331  // Note: ignoring the fact headers are only supposed to contain ASCII.
332  // We should trim out UTF-8.
333  header_value.erase(header_value.find_last_not_of(" \r\n\t") + 1);
334 
335  // Record the line in our header structure. Will be returned as part
336  // of the response info object.
337  auto iter = m_headers.find(header_name);
338  if (iter == m_headers.end()) {
339  m_headers.insert(iter, {header_name, {header_value}});
340  } else {
341  iter->second.push_back(header_value);
342  }
343 
344  if (header_name == "Allow") {
345  std::string_view val(header_value);
346  while (!val.empty()) {
347  auto found = val.find(',');
348  auto method = val.substr(0, found);
349  if (method == "PROPFIND") {
350  auto new_verbs = static_cast<unsigned>(m_allow_verbs) | static_cast<unsigned>(VerbsCache::HttpVerb::kPROPFIND);
351  m_allow_verbs = static_cast<VerbsCache::HttpVerb>(new_verbs);
352  }
353  if (found == std::string_view::npos) break;
354  val = val.substr(found + 1);
355  }
356  if (static_cast<unsigned>(m_allow_verbs) & ~static_cast<unsigned>(VerbsCache::HttpVerb::kUnknown)) {
357  m_allow_verbs = static_cast<VerbsCache::HttpVerb>(static_cast<unsigned>(m_allow_verbs) & ~static_cast<unsigned>(VerbsCache::HttpVerb::kUnknown));
358  }
359  } else if (header_name == "Content-Length") {
360  try {
361  m_content_length = std::stoll(header_value);
362  } catch (...) {
363  return false;
364  }
365  }
366  else if (header_name == "Content-Type") {
367  std::string_view val(header_value);
368  auto found = val.find(";");
369  auto first_type = val.substr(0, found);
370  m_multipart_byteranges = first_type == "multipart/byteranges";
371  if (m_multipart_byteranges) {
372  auto remainder = val.substr(found + 1);
373  found = remainder.find("boundary=");
374  if (found != std::string_view::npos) {
375  SetMultipartSeparator(remainder.substr(found + 9));
376  }
377  }
378  }
379  else if (header_name == "Content-Range") {
380  auto found = header_value.find(" ");
381  if (found == std::string::npos) {
382  return false;
383  }
384  std::string range_unit = header_value.substr(0, found);
385  if (range_unit != "bytes") {
386  return false;
387  }
388  auto range_resp = header_value.substr(found + 1);
389  found = range_resp.find("/");
390  if (found == std::string::npos) {
391  return false;
392  }
393  auto incl_range = range_resp.substr(0, found);
394  found = incl_range.find("-");
395  if (found == std::string::npos) {
396  return false;
397  }
398  auto first_pos = incl_range.substr(0, found);
399  try {
400  m_response_offset = std::stoll(first_pos);
401  } catch (...) {
402  return false;
403  }
404  auto last_pos = incl_range.substr(found + 1);
405  size_t last_byte;
406  try {
407  last_byte = std::stoll(last_pos);
408  } catch (...) {
409  return false;
410  }
411  m_content_length = last_byte - m_response_offset + 1;
412  }
413  else if (header_name == "Location") {
414  m_location = header_value;
415  } else if (header_name == "Digest") {
416  ParseDigest(header_value, m_checksums);
417  }
418  else if (header_name == "Etag")
419  {
420  // Note, the original hader name is ETag, renamed to Etag in parsing
421  // remove additional quotes
422  m_etag = header_value;
423  m_etag.erase(remove(m_etag.begin(), m_etag.end(), '\"'), m_etag.end());
424  }
425  else if (header_name == "Cache-Control")
426  {
427  m_cache_control = header_value;
428  }
429 
430  return true;
431 }
void getline(uchar *buff, int blen)
void SetMultipartSeparator(const std::string_view &sep)
static void ParseDigest(const std::string &digest, XrdClHttp::ChecksumInfo &info)
static bool Canonicalize(std::string &headerName)

References Canonicalize(), getline(), XrdClHttp::VerbsCache::kPROPFIND, XrdClHttp::VerbsCache::kUnknown, ParseDigest(), and SetMultipartSeparator().

+ Here is the call graph for this function:

◆ ParseDigest()

void HeaderParser::ParseDigest ( const std::string &  digest,
XrdClHttp::ChecksumInfo info 
)
static

Definition at line 436 of file XrdClHttpUtil.cc.

436  {
437  std::string_view view(digest);
438  std::array<unsigned char, 32> checksum_value;
439  std::string digest_lower;
440  while (!view.empty()) {
441  auto nextsep = view.find(',');
442  auto entry = view.substr(0, nextsep);
443  if (nextsep == std::string_view::npos) {
444  view = "";
445  } else {
446  view = view.substr(nextsep + 1);
447  }
448  nextsep = entry.find('=');
449  auto name = entry.substr(0, nextsep);
450  auto value = entry.substr(nextsep + 1);
451  digest_lower.clear();
452  digest_lower.resize(name.size());
453  std::transform(name.begin(), name.end(), digest_lower.begin(), [](unsigned char c) {
454  return std::tolower(c);
455  });
456  if (digest_lower == "md5") {
457  if (value.size() != 24) {
458  continue;
459  }
460  if (Base64Decode(value, checksum_value)) {
461  info.Set(XrdClHttp::ChecksumType::kMD5, checksum_value);
462  }
463  } else if (digest_lower == "crc32c") {
464  // XRootD currently incorrectly base64-encodes crc32c checksums; see
465  // https://github.com/xrootd/xrootd/issues/2456
466  // For backward comaptibility, if this looks like base64 encoded (8
467  // bytes long and last two bytes are padding), then we base64 decode.
468  if (value.size() == 8 && value[6] == '=' && value[7] == '=') {
469  if (Base64Decode(value, checksum_value)) {
470  info.Set(XrdClHttp::ChecksumType::kCRC32C, checksum_value);
471  }
472  continue;
473  }
474  std::size_t pos{0};
475  unsigned long val;
476  try {
477  val = std::stoul(value.data(), &pos, 16);
478  } catch (...) {
479  continue;
480  }
481  if (pos == value.size()) {
482  checksum_value[0] = (val >> 24) & 0xFF;
483  checksum_value[1] = (val >> 16) & 0xFF;
484  checksum_value[2] = (val >> 8) & 0xFF;
485  checksum_value[3] = val & 0xFF;
486  info.Set(XrdClHttp::ChecksumType::kCRC32C, checksum_value);
487  }
488  }
489  }
490 }
bool Set(ChecksumType ctype, const std::array< unsigned char, g_max_checksum_length > &value)
static bool Base64Decode(std::string_view input, std::array< unsigned char, 32 > &output)

References Base64Decode(), and XrdClHttp::ChecksumInfo::Set().

Referenced by Parse().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ SetMultipartSeparator()

void XrdClHttp::HeaderParser::SetMultipartSeparator ( const std::string_view &  sep)
inline

Definition at line 105 of file XrdClHttpUtil.hh.

105  {
106  m_multipart_sep = "--" + std::string(sep);
107  m_multipart_byteranges = true;
108  }

Referenced by Parse(), and XrdClHttp::CurlVectorReadOp::SetSeparator().

+ Here is the caller graph for this function:

◆ SetStatusCode()

void XrdClHttp::HeaderParser::SetStatusCode ( int  sc)
inline

Definition at line 94 of file XrdClHttpUtil.hh.

94 {m_status_code = sc;}

Referenced by XrdClHttp::CurlVectorReadOp::SetStatusCode().

+ Here is the caller graph for this function:

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