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::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) {
497 return "MD5";
499 return "CRC32c";
501 return "SHA";
503 return "SHA-256";
504 default:
505 return "";
506 }
507}

References XrdClHttp::kCRC32C, XrdClHttp::kMD5, XrdClHttp::kSHA1, and XrdClHttp::kSHA256.

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 }

◆ GetCacheControl()

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

Definition at line 119 of file XrdClHttpUtil.hh.

119{return m_cache_control;}

◆ GetChecksums()

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

Definition at line 122 of file XrdClHttpUtil.hh.

122{return m_checksums;}

◆ GetContentLength()

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

Definition at line 77 of file XrdClHttpUtil.hh.

77{return m_content_length;}

◆ GetETag()

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

Definition at line 118 of file XrdClHttpUtil.hh.

118{return m_etag;}

◆ GetLocation()

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

Definition at line 117 of file XrdClHttpUtil.hh.

117{return m_location;}

◆ GetOffset()

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

Definition at line 79 of file XrdClHttpUtil.hh.

79{return m_response_offset;}

◆ GetStatusCode()

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

Definition at line 90 of file XrdClHttpUtil.hh.

90{return m_status_code;}

◆ GetStatusMessage()

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

Definition at line 115 of file XrdClHttpUtil.hh.

115{return m_resp_message;}

◆ 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;}

◆ 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;}

◆ 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 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 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(), XrdClHttp::kCRC32C, XrdClHttp::kMD5, 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().

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;}

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