url.cpp raw

   1  // Copyright (c) 2015-present The Bitcoin Core developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  #include <common/url.h>
   6  
   7  #include <charconv>
   8  #include <string>
   9  #include <string_view>
  10  #include <system_error>
  11  
  12  std::string UrlDecode(std::string_view url_encoded)
  13  {
  14      std::string res;
  15      res.reserve(url_encoded.size());
  16  
  17      for (size_t i = 0; i < url_encoded.size(); ++i) {
  18          char c = url_encoded[i];
  19          // Special handling for percent which should be followed by two hex digits
  20          // representing an octet values, see RFC 3986, Section 2.1 Percent-Encoding
  21          if (c == '%' && i + 2 < url_encoded.size()) {
  22              unsigned int decoded_value{0};
  23              auto [p, ec] = std::from_chars(url_encoded.data() + i + 1, url_encoded.data() + i + 3, decoded_value, 16);
  24  
  25              // Only if there is no error and the pointer is set to the end of
  26              // the string, we can be sure both characters were valid hex
  27              if (ec == std::errc{} && p == url_encoded.data() + i + 3) {
  28                  res += static_cast<char>(decoded_value);
  29                  // Next two characters are part of the percent encoding
  30                  i += 2;
  31                  continue;
  32              }
  33              // In case of invalid percent encoding, add the '%' and continue
  34          }
  35          res += c;
  36      }
  37  
  38      return res;
  39  }
  40  
  41  std::string UrlEncode(std::string_view str)
  42  {
  43      std::string res;
  44      res.reserve(str.size() * 3); // worst case: every char needs encoding
  45  
  46      for (char ch : str) {
  47          auto c = static_cast<unsigned char>(ch);
  48          // Unreserved characters per RFC 3986, Section 2.3
  49          if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
  50              (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' || c == '~') {
  51              res += ch;
  52          } else {
  53              // Percent-encode all other characters
  54              res += '%';
  55              constexpr char hex_chars[] = "0123456789ABCDEF";
  56              res += hex_chars[(c >> 4) & 0xF];
  57              res += hex_chars[c & 0xF];
  58          }
  59      }
  60      return res;
  61  }
  62