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
time_t GetModTime () 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 94 of file XrdClHttpUtil.hh.

Constructor & Destructor Documentation

◆ HeaderParser()

XrdClHttp::HeaderParser::HeaderParser ( )
inline

Definition at line 96 of file XrdClHttpUtil.hh.

96{}

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

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

Referenced by ParseDigest().

Here is the caller graph for this function:

◆ Canonicalize()

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

Definition at line 677 of file XrdClHttpUtil.cc.

678{
679 auto upper = true;
680 const static int toLower = 'a' - 'A';
681 for (size_t idx=0; idx<headerName.size(); idx++) {
682 char c = headerName[idx];
683 if (!validHeaderByte(c)) {
684 return false;
685 }
686 if (upper && 'a' <= c && c <= 'z') {
687 c -= toLower;
688 } else if (!upper && 'A' <= c && c <= 'Z') {
689 c += toLower;
690 }
691 headerName[idx] = c;
692 upper = c == '-';
693 }
694 return true;
695}

Referenced by Parse().

Here is the caller graph for this function:

◆ ChecksumTypeToDigestName()

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

Definition at line 617 of file XrdClHttpUtil.cc.

617 {
618 switch (type) {
620 return "adler32";
622 return "crc32";
624 return "MD5";
626 return "CRC32c";
628 return "SHA";
630 return "SHA-256";
632 std::string result;
633 for (int value = 0;
634 value < static_cast<int>(XrdClHttp::ChecksumType::kAll);
635 ++value) {
636 if (!result.empty()) result += ',';
637 result += ChecksumTypeToDigestName(
638 static_cast<XrdClHttp::ChecksumType>(value));
639 }
640 return result;
641 }
642 default:
643 return "";
644 }
645}
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 136 of file XrdClHttpUtil.hh.

137 {
138 return VerbsCache::HttpVerbs(m_allow_verbs);
139 }

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

145{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 148 of file XrdClHttpUtil.hh.

148{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 100 of file XrdClHttpUtil.hh.

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

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

143{return m_location;}

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

Here is the caller graph for this function:

◆ GetModTime()

time_t XrdClHttp::HeaderParser::GetModTime ( ) const
inline

Definition at line 103 of file XrdClHttpUtil.hh.

103{return m_modtime;}

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

Here is the caller graph for this function:

◆ GetOffset()

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

Definition at line 105 of file XrdClHttpUtil.hh.

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

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

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

109{return m_recv_all_headers;}

◆ IsMultipartByterange()

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

Definition at line 123 of file XrdClHttpUtil.hh.

123{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 114 of file XrdClHttpUtil.hh.

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

◆ MultipartSeparator()

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

Definition at line 127 of file XrdClHttpUtil.hh.

127{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 379 of file XrdClHttpUtil.cc.

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

543 {
544 std::string_view view(digest);
545 std::array<unsigned char, g_max_checksum_length> checksum_value{};
546 std::string digest_lower;
547 while (!view.empty()) {
548 auto nextsep = view.find(',');
549 auto entry = view.substr(0, nextsep);
550 if (nextsep == std::string_view::npos) {
551 view = "";
552 } else {
553 view = view.substr(nextsep + 1);
554 }
555 nextsep = entry.find('=');
556 if (nextsep == std::string_view::npos) continue;
557 auto name = trim_view(entry.substr(0, nextsep));
558 auto value = trim_view(entry.substr(nextsep + 1));
559 digest_lower.clear();
560 digest_lower.resize(name.size());
561 std::transform(name.begin(), name.end(), digest_lower.begin(), [](unsigned char c) {
562 return std::tolower(c);
563 });
564 const auto setHex32 = [&](ChecksumType type) {
565 if (value.empty() || value.size() > 8) return;
566 std::string padded(8 - value.size(), '0');
567 padded.append(value.data(), value.size());
568 XrdCksData checksum;
569 if (!checksum.Set(padded.c_str(), padded.size())) return;
570 std::copy_n(
571 reinterpret_cast<const unsigned char *>(checksum.Value),
572 GetChecksumLength(type), checksum_value.begin());
573 info.Set(type, checksum_value);
574 };
575 const auto setBase64 = [&](ChecksumType type) {
576 const size_t decodedSize = GetChecksumLength(type);
577 const size_t encodedSize = 4 * ((decodedSize + 2) / 3);
578 if (value.size() == encodedSize
579 && Base64Decode(value, checksum_value))
580 {
581 info.Set(type, checksum_value);
582 }
583 };
584 if (digest_lower == "adler" || digest_lower == "adler32") {
585 setHex32(ChecksumType::kADLER32);
586 } else if (digest_lower == "crc32") {
587 // Accept padded base64 alongside the existing hexadecimal form.
588 // Require "==" to distinguish a four-byte base64 checksum from
589 // short hexadecimal values, which may also be valid unpadded base64.
590 if (value.size() == 8 && value[6] == '=' && value[7] == '=') {
591 setBase64(ChecksumType::kCRC32);
592 continue;
593 }
594 setHex32(ChecksumType::kCRC32);
595 } else if (digest_lower == "md5") {
596 setBase64(ChecksumType::kMD5);
597 } else if (digest_lower == "sha" || digest_lower == "sha1") {
598 setBase64(ChecksumType::kSHA1);
599 } else if (digest_lower == "sha-256" || digest_lower == "sha256") {
600 setBase64(ChecksumType::kSHA256);
601 } else if (digest_lower == "crc32c") {
602 // XRootD currently incorrectly base64-encodes crc32c checksums; see
603 // https://github.com/xrootd/xrootd/issues/2456
604 // For backward comaptibility, if this looks like base64 encoded (8
605 // bytes long and last two bytes are padding), then we base64 decode.
606 if (value.size() == 8 && value[6] == '=' && value[7] == '=') {
607 setBase64(ChecksumType::kCRC32C);
608 continue;
609 }
610 setHex32(ChecksumType::kCRC32C);
611 }
612 }
613}
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 131 of file XrdClHttpUtil.hh.

131 {
132 m_multipart_sep = "--" + std::string(sep);
133 m_multipart_byteranges = true;
134 }

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

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