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 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) {
493 return "MD5";
495 return "CRC32c";
497 return "SHA";
499 return "SHA-256";
500 default:
501 return "";
502 }
503}

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 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 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 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(), 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: