XRootD
Loading...
Searching...
No Matches
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::ChecksumInfo & GetChecksums () 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, g_max_checksum_length > &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 93 of file XrdClHttpUtil.hh.

Constructor & Destructor Documentation

◆ HeaderParser()

XrdClHttp::HeaderParser::HeaderParser ( )
inline

Definition at line 95 of file XrdClHttpUtil.hh.

95{}

Referenced by XrdClHttp::CurlOperation::Redirect(), and XrdClHttp::CurlOperation::SetupNextRequest().

Here is the caller graph for this function:

Member Function Documentation

◆ Base64Decode()

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

Definition at line 343 of file XrdClHttpUtil.cc.

345 {
346 if (input.size() > 4 * ((output.size() + 2) / 3)
347 || input.size() % 4 != 0)
348 return false;
349 if (input.size() == 0) return true;
350
351 std::unique_ptr<BIO, decltype(&BIO_free_all)> b64(BIO_new(BIO_f_base64()), &BIO_free_all);
352 BIO_set_flags(b64.get(), BIO_FLAGS_BASE64_NO_NL);
353 std::unique_ptr<BIO, decltype(&BIO_free_all)> bmem(
354 BIO_new_mem_buf(const_cast<char *>(input.data()), input.size()), &BIO_free_all);
355 bmem.reset(BIO_push(b64.release(), bmem.release()));
356
357 // Compute expected length of output; used to verify BIO_read consumes all input
358 size_t expectedLen = static_cast<size_t>(input.size() * 0.75);
359 if (input[input.size() - 1] == '=') {
360 expectedLen -= 1;
361 if (input[input.size() - 2] == '=') {
362 expectedLen -= 1;
363 }
364 }
365
366 auto len = BIO_read(bmem.get(), &output[0], output.size());
367
368 if (len == -1 || static_cast<size_t>(len) != expectedLen) return false;
369
370 return true;
371}

Referenced by ParseDigest().

Here is the caller graph for this function:

◆ Canonicalize()

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

Definition at line 664 of file XrdClHttpUtil.cc.

665{
666 auto upper = true;
667 const static int toLower = 'a' - 'A';
668 for (size_t idx=0; idx<headerName.size(); idx++) {
669 char c = headerName[idx];
670 if (!validHeaderByte(c)) {
671 return false;
672 }
673 if (upper && 'a' <= c && c <= 'z') {
674 c -= toLower;
675 } else if (!upper && 'A' <= c && c <= 'Z') {
676 c += toLower;
677 }
678 headerName[idx] = c;
679 upper = c == '-';
680 }
681 return true;
682}

Referenced by Parse().

Here is the caller graph for this function:

◆ ChecksumTypeToDigestName()

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

Definition at line 604 of file XrdClHttpUtil.cc.

604 {
605 switch (type) {
607 return "adler32";
609 return "crc32";
611 return "MD5";
613 return "CRC32c";
615 return "SHA";
617 return "SHA-256";
619 std::string result;
620 for (int value = 0;
621 value < static_cast<int>(XrdClHttp::ChecksumType::kAll);
622 ++value) {
623 if (!result.empty()) result += ',';
624 result += ChecksumTypeToDigestName(
625 static_cast<XrdClHttp::ChecksumType>(value));
626 }
627 return result;
628 }
629 default:
630 return "";
631 }
632}
static std::string ChecksumTypeToDigestName(XrdClHttp::ChecksumType type)

References ChecksumTypeToDigestName(), XrdClHttp::kADLER32, XrdClHttp::kAll, XrdClHttp::kCRC32, XrdClHttp::kCRC32C, XrdClHttp::kMD5, XrdClHttp::kSHA1, and XrdClHttp::kSHA256.

Referenced by ChecksumTypeToDigestName(), and XrdClHttp::CurlChecksumOp::Setup().

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

◆ GetAllowedVerbs()

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

Definition at line 132 of file XrdClHttpUtil.hh.

133 {
134 return VerbsCache::HttpVerbs(m_allow_verbs);
135 }

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 141 of file XrdClHttpUtil.hh.

141{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 144 of file XrdClHttpUtil.hh.

144{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 99 of file XrdClHttpUtil.hh.

99{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 140 of file XrdClHttpUtil.hh.

140{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 139 of file XrdClHttpUtil.hh.

139{return m_location;}

Referenced by XrdClHttp::CurlStatOp::OptionsDone(), and XrdClHttp::CurlOperation::Redirect().

Here is the caller graph for this function:

◆ GetOffset()

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

Definition at line 101 of file XrdClHttpUtil.hh.

101{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 112 of file XrdClHttpUtil.hh.

112{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 137 of file XrdClHttpUtil.hh.

137{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 105 of file XrdClHttpUtil.hh.

105{return m_recv_all_headers;}

◆ IsMultipartByterange()

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

Definition at line 119 of file XrdClHttpUtil.hh.

119{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 110 of file XrdClHttpUtil.hh.

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

◆ MultipartSeparator()

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

Definition at line 123 of file XrdClHttpUtil.hh.

123{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 378 of file XrdClHttpUtil.cc.

379{
380 if (m_recv_all_headers) {
381 m_recv_all_headers = false;
382 m_recv_status_line = false;
383 }
384
385 if (!m_recv_status_line) {
386 m_recv_status_line = true;
387
388 std::stringstream ss(header_line);
389 std::string item;
390 if (!std::getline(ss, item, ' ')) return false;
391 m_resp_protocol = item;
392 if (!std::getline(ss, item, ' ')) return false;
393 try {
394 m_status_code = std::stol(item);
395 } catch (...) {
396 return false;
397 }
398 if (m_status_code < 100 || m_status_code >= 600) {
399 return false;
400 }
401 if (!std::getline(ss, item, '\n')) return false;
402 auto cr_loc = item.find('\r');
403 if (cr_loc != std::string::npos) {
404 m_resp_message = item.substr(0, cr_loc);
405 } else {
406 m_resp_message = item;
407 }
408 return true;
409 }
410
411 if (header_line.empty() || header_line == "\n" || header_line == "\r\n") {
412 m_recv_all_headers = true;
413 return true;
414 }
415
416 auto found = header_line.find(":");
417 if (found == std::string::npos) {
418 return false;
419 }
420
421 std::string header_name = header_line.substr(0, found);
422 if (!Canonicalize(header_name)) {
423 return false;
424 }
425
426 found += 1;
427 while (found < header_line.size()) {
428 if (header_line[found] != ' ') {break;}
429 found += 1;
430 }
431 std::string header_value = header_line.substr(found);
432 // Note: ignoring the fact headers are only supposed to contain ASCII.
433 // We should trim out UTF-8.
434 header_value.erase(header_value.find_last_not_of(" \r\n\t") + 1);
435
436 // Record the line in our header structure. Will be returned as part
437 // of the response info object.
438 auto iter = m_headers.find(header_name);
439 if (iter == m_headers.end()) {
440 m_headers.insert(iter, {header_name, {header_value}});
441 } else {
442 iter->second.push_back(header_value);
443 }
444
445 if (header_name == "Allow") {
446 std::string_view val(header_value);
447 while (!val.empty()) {
448 auto found = val.find(',');
449 auto method = val.substr(0, found);
450 if (method == "PROPFIND") {
451 auto new_verbs = static_cast<unsigned>(m_allow_verbs) | static_cast<unsigned>(VerbsCache::HttpVerb::kPROPFIND);
452 m_allow_verbs = static_cast<VerbsCache::HttpVerb>(new_verbs);
453 }
454 if (found == std::string_view::npos) break;
455 val = val.substr(found + 1);
456 }
457 if (static_cast<unsigned>(m_allow_verbs) & ~static_cast<unsigned>(VerbsCache::HttpVerb::kUnknown)) {
458 m_allow_verbs = static_cast<VerbsCache::HttpVerb>(static_cast<unsigned>(m_allow_verbs) & ~static_cast<unsigned>(VerbsCache::HttpVerb::kUnknown));
459 }
460 } else if (header_name == "Content-Length") {
461 try {
462 m_content_length = std::stoll(header_value);
463 } catch (...) {
464 return false;
465 }
466 }
467 else if (header_name == "Content-Type") {
468 std::string_view val(header_value);
469 auto found = val.find(";");
470 auto first_type = val.substr(0, found);
471 m_multipart_byteranges = first_type == "multipart/byteranges";
472 if (m_multipart_byteranges) {
473 auto remainder = val.substr(found + 1);
474 found = remainder.find("boundary=");
475 if (found != std::string_view::npos) {
476 SetMultipartSeparator(remainder.substr(found + 9));
477 }
478 }
479 }
480 else if (header_name == "Content-Range") {
481 auto found = header_value.find(" ");
482 if (found == std::string::npos) {
483 return false;
484 }
485 std::string range_unit = header_value.substr(0, found);
486 if (range_unit != "bytes") {
487 return false;
488 }
489 auto range_resp = header_value.substr(found + 1);
490 found = range_resp.find("/");
491 if (found == std::string::npos) {
492 return false;
493 }
494 auto incl_range = range_resp.substr(0, found);
495 found = incl_range.find("-");
496 if (found == std::string::npos) {
497 return false;
498 }
499 auto first_pos = incl_range.substr(0, found);
500 try {
501 m_response_offset = std::stoll(first_pos);
502 } catch (...) {
503 return false;
504 }
505 auto last_pos = incl_range.substr(found + 1);
506 size_t last_byte;
507 try {
508 last_byte = std::stoll(last_pos);
509 } catch (...) {
510 return false;
511 }
512 m_content_length = last_byte - m_response_offset + 1;
513 }
514 else if (header_name == "Location") {
515 m_location = header_value;
516 } else if (header_name == "Digest") {
517 ParseDigest(header_value, m_checksums);
518 }
519 else if (header_name == "Etag")
520 {
521 // Note, the original hader name is ETag, renamed to Etag in parsing
522 // remove additional quotes
523 m_etag = header_value;
524 m_etag.erase(remove(m_etag.begin(), m_etag.end(), '\"'), m_etag.end());
525 }
526 else if (header_name == "Cache-Control")
527 {
528 m_cache_control = header_value;
529 }
530
531 return true;
532}
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(), 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 537 of file XrdClHttpUtil.cc.

537 {
538 std::string_view view(digest);
539 std::array<unsigned char, g_max_checksum_length> checksum_value{};
540 std::string digest_lower;
541 while (!view.empty()) {
542 auto nextsep = view.find(',');
543 auto entry = view.substr(0, nextsep);
544 if (nextsep == std::string_view::npos) {
545 view = "";
546 } else {
547 view = view.substr(nextsep + 1);
548 }
549 nextsep = entry.find('=');
550 if (nextsep == std::string_view::npos) continue;
551 auto name = trim_view(entry.substr(0, nextsep));
552 auto value = trim_view(entry.substr(nextsep + 1));
553 digest_lower.clear();
554 digest_lower.resize(name.size());
555 std::transform(name.begin(), name.end(), digest_lower.begin(), [](unsigned char c) {
556 return std::tolower(c);
557 });
558 const auto setHex32 = [&](ChecksumType type) {
559 if (value.empty() || value.size() > 8) return;
560 std::string padded(8 - value.size(), '0');
561 padded.append(value.data(), value.size());
562 XrdCksData checksum;
563 if (!checksum.Set(padded.c_str(), padded.size())) return;
564 std::copy_n(
565 reinterpret_cast<const unsigned char *>(checksum.Value),
566 GetChecksumLength(type), checksum_value.begin());
567 info.Set(type, checksum_value);
568 };
569 const auto setBase64 = [&](ChecksumType type) {
570 const size_t decodedSize = GetChecksumLength(type);
571 const size_t encodedSize = 4 * ((decodedSize + 2) / 3);
572 if (value.size() == encodedSize
573 && Base64Decode(value, checksum_value))
574 {
575 info.Set(type, checksum_value);
576 }
577 };
578 if (digest_lower == "adler" || digest_lower == "adler32") {
579 setHex32(ChecksumType::kADLER32);
580 } else if (digest_lower == "crc32") {
581 setHex32(ChecksumType::kCRC32);
582 } else if (digest_lower == "md5") {
583 setBase64(ChecksumType::kMD5);
584 } else if (digest_lower == "sha" || digest_lower == "sha1") {
585 setBase64(ChecksumType::kSHA1);
586 } else if (digest_lower == "sha-256" || digest_lower == "sha256") {
587 setBase64(ChecksumType::kSHA256);
588 } else if (digest_lower == "crc32c") {
589 // XRootD currently incorrectly base64-encodes crc32c checksums; see
590 // https://github.com/xrootd/xrootd/issues/2456
591 // For backward comaptibility, if this looks like base64 encoded (8
592 // bytes long and last two bytes are padding), then we base64 decode.
593 if (value.size() == 8 && value[6] == '=' && value[7] == '=') {
594 setBase64(ChecksumType::kCRC32C);
595 continue;
596 }
597 setHex32(ChecksumType::kCRC32C);
598 }
599 }
600}
char Value[ValuSize]
Definition XrdCksData.hh:53
int Set(const char *csName)
Definition XrdCksData.hh:81
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, g_max_checksum_length > &output)
size_t GetChecksumLength(ChecksumType ctype)
std::string_view trim_view(const std::string_view &input_view)

References Base64Decode(), XrdClHttp::GetChecksumLength(), XrdClHttp::kADLER32, XrdClHttp::kCRC32, XrdClHttp::kCRC32C, XrdClHttp::kMD5, XrdClHttp::kSHA1, XrdClHttp::kSHA256, XrdCksData::Set(), XrdClHttp::ChecksumInfo::Set(), XrdClHttp::trim_view(), and XrdCksData::Value.

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 127 of file XrdClHttpUtil.hh.

127 {
128 m_multipart_sep = "--" + std::string(sep);
129 m_multipart_byteranges = true;
130 }

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 116 of file XrdClHttpUtil.hh.

116{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: