XRootD
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 
24 #include "XrdOuc/XrdOucTUtils.hh"
25 #include "XrdOuc/XrdOucUtils.hh"
26 #include <exception>
27 #include <algorithm>
28 
29 std::map<std::string,XrdHttpChecksumHandlerImpl::XrdHttpChecksumPtr> XrdHttpChecksumHandlerImpl::XROOTD_DIGEST_NAME_TO_CKSUMS;
30 
31 void 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 
45 void 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 
90 const std::vector<std::string> &XrdHttpChecksumHandlerImpl::getNonIANAConfiguredCksums() const {
91  return mNonIANAConfiguredChecksums;
92 }
93 
94 const std::vector<XrdHttpChecksumHandler::XrdHttpChecksumRawPtr> & XrdHttpChecksumHandlerImpl::getConfiguredChecksums() const {
95  return mConfiguredChecksums;
96 }
97 
98 
99 void XrdHttpChecksumHandlerImpl::configure(const char *csList) {
100  initializeCksumsMaps();
101  if(csList != nullptr) {
102  initializeXRootDConfiguredCksums(csList);
103  }
104 }
105 
106 void 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 
127 std::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 
134 std::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.
Definition: XrdOucTUtils.hh:51
static void trim(std::string &str)