15 using namespace std::string_view_literals;
23 constexpr std::array forbidden_headers{
29 "proxy-authenticate"sv,
30 "proxy-authorization"sv,
34 "transfer-encoding"sv,
39 constexpr std::string_view transfer_header_prefix{
"transferheader"};
42 constexpr std::string_view tchar{
45 "abcdefghijklmnopqrstuvwxyz"
46 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
50 constexpr std::string_view ows{
" \t"};
53 std::string_view
trim(
const std::string_view text,
const std::string_view strip)
55 const auto discard = [strip](
const char c) {
56 return strip.find(c) != std::string_view::npos;
59 const auto first = std::find_if_not(text.begin(), text.end(), discard);
60 const auto last = std::find_if_not(text.rbegin(),
61 std::make_reverse_iterator(first), discard).base();
63 return text.substr(first - text.begin(), last - first);
69 namespace HeaderBuilder {
76 std::string_view bare(name);
77 while (
CompareIgnoreCase(bare.substr(0, transfer_header_prefix.size()), transfer_header_prefix)) {
78 bare.remove_prefix(transfer_header_prefix.size());
81 return std::find_if(forbidden_headers.begin(), forbidden_headers.end(), [bare](
auto _sv) {
82 return CompareIgnoreCase(bare, _sv);
84 != forbidden_headers.end();
90 const auto to_ascii_lower = [](
const unsigned char c) {
91 return (c >=
'A' && c <=
'Z') ?
static_cast<unsigned char>(c -
'A' +
'a') : c;
94 return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(),
95 [to_ascii_lower](
const unsigned char a,
const unsigned char b) {
96 return to_ascii_lower(a) == to_ascii_lower(b);
103 for (
const auto &header :
extra) {
104 const auto present = std::any_of(headers.cbegin(), headers.cend(),
105 [&header](
const auto &existing) {
106 return CompareIgnoreCase(existing.first, header.first);
109 headers.emplace_back(header);
126 for (
auto pos = spec.begin(); pos != spec.end(); ) {
127 const auto eol = std::find(pos, spec.end(),
'\n');
130 const auto entry =
trim(spec.substr(pos - spec.begin(), eol - pos),
" \t\r");
131 pos = (eol == spec.end() ? eol : std::next(eol));
141 const auto colon = entry.find(
':');
142 if (colon == std::string_view::npos) {
143 log->
Error(
kLogXrdClHttp,
"Requested header %s holds no colon; each header must read \"<name>: <value>\"",
150 const std::string name(
trim(entry.substr(0, colon), ows));
156 if (!std::all_of(name.begin(), name.end(), [](
const char c) {
157 return tchar.find(c) != std::string_view::npos;
160 log->
Error(
kLogXrdClHttp,
"Requested header %s holds a name that is not an HTTP token", name.c_str());
165 log->
Error(
kLogXrdClHttp,
"Requested header %s must not be set by the client", name.c_str());
172 const auto value =
trim(entry.substr(colon + 1), ows);
178 if (value.find(
'\r') != std::string_view::npos) {
179 log->
Error(
kLogXrdClHttp,
"Requested header %s holds a carriage return in its value", name.c_str());
184 requested.emplace_back(name, value);
191 if (requested.empty()) {
198 for (
const auto &header : requested) {
199 if (!names.empty()) names +=
", ";
200 names += header.first;
204 headers = std::move(requested);
void trim(std::string &str)
std::string obfuscateAuth(const std::string &input)
static Log * GetLog()
Get default log.
void Error(uint64_t topic, const char *format,...)
Report an error.
void Debug(uint64_t topic, const char *format,...)
Print a debug message.
const uint64_t kLogXrdClHttp