XRootD
Loading...
Searching...
No Matches
XrdHttpChecksumHandler.cc
Go to the documentation of this file.
1//------------------------------------------------------------------------------
2// This file is part of XrdHTTP: A pragmatic implementation of the
3// HTTP/WebDAV protocol for the Xrootd framework
4//
5// Copyright (c) 2013 by European Organization for Nuclear Research (CERN)
6// Author: Cedric Caffy <ccaffy@cern.ch>
7// File Date: Mar 2023
8//------------------------------------------------------------------------------
9// XRootD is free software: you can redistribute it and/or modify
10// it under the terms of the GNU Lesser General Public License as published by
11// the Free Software Foundation, either version 3 of the License, or
12// (at your option) any later version.
13//
14// XRootD is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17// GNU General Public License for more details.
18//
19// You should have received a copy of the GNU Lesser General Public License
20// along with XRootD. If not, see <http://www.gnu.org/licenses/>.
21//------------------------------------------------------------------------------
22
25#include "XrdOuc/XrdOucUtils.hh"
26#include <exception>
27#include <algorithm>
28
29std::map<std::string,XrdHttpChecksumHandlerImpl::XrdHttpChecksumPtr> XrdHttpChecksumHandlerImpl::XROOTD_DIGEST_NAME_TO_CKSUMS;
30
31void XrdHttpChecksumHandlerImpl::initializeCksumsMaps() {
32 addChecksumToMaps(std::make_unique<XrdHttpChecksum>("md5","md5",true));
33 addChecksumToMaps(std::make_unique<XrdHttpChecksum>("adler32","adler32",false));
34 addChecksumToMaps(std::make_unique<XrdHttpChecksum>("sha1","sha",true));
35 addChecksumToMaps(std::make_unique<XrdHttpChecksum>("sha256","sha-256",true));
36 addChecksumToMaps(std::make_unique<XrdHttpChecksum>("sha512","sha-512",true));
37 addChecksumToMaps(std::make_unique<XrdHttpChecksum>("cksum","UNIXcksum",false));
38 addChecksumToMaps(std::make_unique<XrdHttpChecksum>("crc32","crc32",false));
39 addChecksumToMaps(std::make_unique<XrdHttpChecksum>("crc32c","crc32c",false));
40 // According to https://www.iana.org/assignments/http-digest-hash-alg/http-digest-hash-alg.xhtml, adler32 is now adler...
41 // We add it to ensure it works and keep retrocompatibility
42 XROOTD_DIGEST_NAME_TO_CKSUMS["adler"] = std::make_unique<XrdHttpChecksum>("adler32","adler",false);
43}
44
45void XrdHttpChecksumHandlerImpl::addChecksumToMaps(XrdHttpChecksumHandlerImpl::XrdHttpChecksumPtr && checksum) {
46 // We also map xrootd-configured checksum's HTTP names to the corresponding checksums --> this will allow
47 // users to configure algorithms like, for example, `sha-512` and be considered as `sha512` algorithm
48 XROOTD_DIGEST_NAME_TO_CKSUMS[checksum->getHttpNameLowerCase()] = std::make_unique<XrdHttpChecksum>(checksum->getHttpNameLowerCase(), checksum->getHttpName(), checksum->needsBase64Padding());
49 XROOTD_DIGEST_NAME_TO_CKSUMS[checksum->getXRootDConfigDigestName()] = std::move(checksum);
50}
51
53 if(!mConfiguredChecksums.empty()) {
54 std::vector<std::string> userDigests = getUserDigests(wantDigest);
55 //Loop over the user digests and find the corresponding checksum
56 for(auto userDigest: userDigests) {
57 auto httpCksum = std::find_if(mConfiguredChecksums.begin(), mConfiguredChecksums.end(),[userDigest](const XrdHttpChecksumRawPtr & cksum){
58 return userDigest == cksum->getHttpNameLowerCase();
59 });
60 if(httpCksum != mConfiguredChecksums.end()) {
61 return *httpCksum;
62 }
63 }
64 return mConfiguredChecksums[0];
65 }
66 //If there are no configured checksums, return nullptr
67 return nullptr;
68}
69
71 if(!mConfiguredChecksums.empty()) {
72 uint8_t bestPref = 0;
73 XrdHttpChecksumHandlerImpl::XrdHttpChecksumRawPtr retCksum = mConfiguredChecksums.front();
74 for(const auto & [digestName, preference]: wantReprDigest) {
75 if(preference > bestPref) {
76 const auto cksumItor = std::find_if(mConfiguredChecksums.begin(), mConfiguredChecksums.end(),[dn = digestName](const XrdHttpChecksumRawPtr & cksum){
77 return dn == cksum->getHttpNameLowerCase();
78 });
79 if(cksumItor != mConfiguredChecksums.end()) {
80 bestPref = preference;
81 retCksum = *cksumItor;
82 }
83 }
84 }
85 return retCksum;
86 }
87 return nullptr;
88}
89
90const std::vector<std::string> &XrdHttpChecksumHandlerImpl::getNonIANAConfiguredCksums() const {
91 return mNonIANAConfiguredChecksums;
92}
93
94const std::vector<XrdHttpChecksumHandler::XrdHttpChecksumRawPtr> & XrdHttpChecksumHandlerImpl::getConfiguredChecksums() const {
95 return mConfiguredChecksums;
96}
97
98
99void XrdHttpChecksumHandlerImpl::configure(const char *csList) {
100 initializeCksumsMaps();
101 if(csList != nullptr) {
102 initializeXRootDConfiguredCksums(csList);
103 }
104}
105
106void XrdHttpChecksumHandlerImpl::initializeXRootDConfiguredCksums(const char *csList) {
107 std::vector<std::string> splittedCslist;
108 XrdOucTUtils::splitString(splittedCslist,csList,",");
109 for(auto csElt: splittedCslist) {
110 auto csName = getElement(csElt,":",1);
111 auto checksumItor = XROOTD_DIGEST_NAME_TO_CKSUMS.find(csName);
112 if(checksumItor != XROOTD_DIGEST_NAME_TO_CKSUMS.end()) {
113 mConfiguredChecksums.push_back(checksumItor->second.get());
114 if(checksumItor->first == "adler32") {
115 // adler32 is configured, this "if" test is ran for each configured checksum, but this is at server start up, no need
116 // to optimize this code...
117 // According to https://www.iana.org/assignments/http-digest-hash-alg/http-digest-hash-alg.xhtml, adler32 is now adler...
118 // We add it to ensure it works and keep retrocompatibility
119 mConfiguredChecksums.push_back(XROOTD_DIGEST_NAME_TO_CKSUMS["adler"].get());
120 }
121 } else {
122 mNonIANAConfiguredChecksums.push_back(csName);
123 }
124 }
125}
126
127std::string XrdHttpChecksumHandlerImpl::getElement(const std::string &input, const std::string & delimiter,
128 const size_t position) {
129 std::vector<std::string> elementsAfterSplit;
130 XrdOucTUtils::splitString(elementsAfterSplit,input,delimiter);
131 return elementsAfterSplit[position];
132}
133
134std::vector<std::string> XrdHttpChecksumHandlerImpl::getUserDigests(const std::string &userDigests) {
135 //userDigest is a comma-separated list with q-values
136 std::vector<std::string> userDigestsRet;
137 std::vector<std::string> userDigestsWithQValues;
138 XrdOucTUtils::splitString(userDigestsWithQValues,userDigests,",");
139 for(auto & userDigestWithQValue: userDigestsWithQValues){
140 std::transform(userDigestWithQValue.begin(),userDigestWithQValue.end(),userDigestWithQValue.begin(),::tolower);
141 auto userDigest = getElement(userDigestWithQValue,";",0);
142 XrdOucUtils::trim(userDigest);
143 userDigestsRet.push_back(userDigest);
144 }
145 return userDigestsRet;
146}
const std::vector< std::string > & getNonIANAConfiguredCksums() const
XrdHttpChecksumRawPtr getChecksumToRunWantDigest(const std::string &wantDigest) const
std::unique_ptr< XrdHttpChecksum > XrdHttpChecksumPtr
XrdHttpChecksumRawPtr getChecksumToRunWantReprDigest(const std::map< std::string, uint8_t > &wantReprDigest) const
void configure(const char *csList)
const std::vector< XrdHttpChecksumRawPtr > & getConfiguredChecksums() const
static void splitString(Container &result, const std::string &input, const std::string &delimiter)
Split a string.
static void trim(std::string &str)