| // Licensed to the Apache Software Foundation (ASF) under one | |
| // or more contributor license agreements. See the NOTICE file | |
| // distributed with this work for additional information | |
| // regarding copyright ownership. The ASF licenses this file | |
| // to you under the Apache License, Version 2.0 (the | |
| // "License"); you may not use this file except in compliance | |
| // with the License. You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, | |
| // software distributed under the License is distributed on an | |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| // KIND, either express or implied. See the License for the | |
| // specific language governing permissions and limitations | |
| // under the License. | |
| #include "util/url_coding.h" | |
| #include <curl/curl.h> | |
| #include <libbase64.h> | |
| #include <cmath> | |
| #include <sstream> | |
| namespace doris { | |
| inline unsigned char to_hex(unsigned char x) { | |
| return x + (x > 9 ? ('A' - 10) : '0'); | |
| } | |
| // Adapted from http://dlib.net/dlib/server/server_http.cpp.html | |
| void url_encode(const std::string_view& in, std::string* out) { | |
| std::ostringstream os; | |
| for (auto c : in) { | |
| // impl as https://docs.oracle.com/javase/8/docs/api/java/net/URLEncoder.html | |
| if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || | |
| c == '.' || c == '-' || c == '*' || c == '_') { // allowed | |
| os << c; | |
| } else if (c == ' ') { | |
| os << '+'; | |
| } else { | |
| ///TODO: In the past, there was an error here involving the modulus operation on a char (signed number). | |
| // When the char data exceeds 128 (UTF-8 byte), it leads to incorrect results. It is actually better to use some third-party libraries here. | |
| os << '%' << to_hex((unsigned char)c >> 4) << to_hex((unsigned char)c % 16); | |
| } | |
| } | |
| *out = os.str(); | |
| } | |
| // Adapted from | |
| // http://www.boost.org/doc/libs/1_40_0/doc/html/boost_asio/ | |
| // example/http/server3/request_handler.cpp | |
| // See http://www.boost.org/LICENSE_1_0.txt for license for this method. | |
| // Value of a single hexadecimal digit, or -1 when c is not one. | |
| static int hex_digit(char c) { | |
| if (c >= '0' && c <= '9') { | |
| return c - '0'; | |
| } | |
| if (c >= 'a' && c <= 'f') { | |
| return c - 'a' + 10; | |
| } | |
| if (c >= 'A' && c <= 'F') { | |
| return c - 'A' + 10; | |
| } | |
| return -1; | |
| } | |
| bool url_decode(const std::string& in, std::string* out) { | |
| out->clear(); | |
| out->reserve(in.size()); | |
| for (size_t i = 0; i < in.size(); ++i) { | |
| if (in[i] == '%') { | |
| if (i + 3 > in.size()) { | |
| return false; | |
| } | |
| // A '%' must be followed by exactly two hexadecimal digits. Parsing the pair | |
| // with a stream accepts a partial match such as "%1g" and silently drops the | |
| // character that follows it. | |
| const int high = hex_digit(in[i + 1]); | |
| const int low = hex_digit(in[i + 2]); | |
| if (high < 0 || low < 0) { | |
| return false; | |
| } | |
| (*out) += static_cast<char>((high << 4) | low); | |
| i += 2; | |
| } else if (in[i] == '+') { | |
| (*out) += ' '; | |
| } else { | |
| (*out) += in[i]; | |
| } | |
| } | |
| return true; | |
| } | |
| void base64_encode(const std::string& in, std::string* out) { | |
| out->resize((size_t)(4.0 * std::ceil(in.length() / 3.0))); | |
| auto len = base64_encode(reinterpret_cast<const unsigned char*>(in.data()), in.length(), | |
| (unsigned char*)out->data()); | |
| out->resize(len); | |
| } | |
| size_t base64_encode(const unsigned char* data, size_t length, unsigned char* encoded_data) { | |
| size_t encode_len = 0; | |
| #if defined(__aarch64__) || defined(_M_ARM64) | |
| do_base64_encode(reinterpret_cast<const char*>(data), length, | |
| reinterpret_cast<char*>(encoded_data), &encode_len, BASE64_FORCE_NEON64); | |
| #else | |
| do_base64_encode(reinterpret_cast<const char*>(data), length, | |
| reinterpret_cast<char*>(encoded_data), &encode_len, 0); | |
| #endif | |
| return encode_len; | |
| } | |
| int64_t base64_decode(const char* data, size_t length, char* decoded_data) { | |
| size_t decode_len = 0; | |
| #if defined(__aarch64__) || defined(_M_ARM64) | |
| auto ret = do_base64_decode(reinterpret_cast<const char*>(data), length, decoded_data, | |
| &decode_len, BASE64_FORCE_NEON64); | |
| #else | |
| auto ret = do_base64_decode(data, length, decoded_data, &decode_len, 0); | |
| #endif | |
| return ret > 0 ? decode_len : -1; | |
| } | |
| bool base64_decode(const std::string& in, std::string* out) { | |
| out->resize(in.length()); | |
| int64_t len = base64_decode(in.data(), in.length(), out->data()); | |
| if (len < 0) { | |
| return false; | |
| } | |
| out->resize(len); | |
| return true; | |
| } | |
| void escape_for_html(const std::string& in, std::stringstream* out) { | |
| for (const auto& c : in) { | |
| switch (c) { | |
| case '<': | |
| (*out) << "<"; | |
| break; | |
| case '>': | |
| (*out) << ">"; | |
| break; | |
| case '&': | |
| (*out) << "&"; | |
| break; | |
| default: | |
| (*out) << c; | |
| } | |
| } | |
| } | |
| std::string escape_for_html_to_string(const std::string& in) { | |
| std::stringstream str; | |
| escape_for_html(in, &str); | |
| return str.str(); | |
| } | |
| } // namespace doris |