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 242 of file XrdClHttpUtil.cc.

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

Referenced by ParseDigest().

+ Here is the caller graph for this function:

◆ Canonicalize()

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

Definition at line 535 of file XrdClHttpUtil.cc.

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

Referenced by Parse().

+ Here is the caller graph for this function:

◆ ChecksumTypeToDigestName()

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

Definition at line 490 of file XrdClHttpUtil.cc.

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

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 273 of file XrdClHttpUtil.cc.

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

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