diff --git a/examples/http_client.cc b/examples/http_client.cc index f7160dd..60dd8f4 100644 --- a/examples/http_client.cc +++ b/examples/http_client.cc @@ -1,4 +1,5 @@ #include +#include #include "webcc/http_client_session.h" #include "webcc/logger.h" @@ -89,6 +90,21 @@ void ExampleCompression() { std::cout << r->content() << std::endl; } +// Get an image from HttpBin.org and save to the given file path. +// E.g., ExampleImage("E:\\example.jpeg") +void ExampleImage(const std::string& path) { + HttpClientSession session; + + auto r = session.Get("http://httpbin.org/image/jpeg"); + + // Or + // auto r = session.Get("http://httpbin.org/image", {}, + // {"Accept", "image/jpeg"}); + + std::ofstream ofs(path, std::ios::binary); + ofs << r->content(); +} + int main() { WEBCC_LOG_INIT("", LOG_CONSOLE); diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt index 793bcda..4e2b963 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -1,14 +1,10 @@ # Unit test set(UT_SRCS + base64_test.cc + rest_service_manager_test.cc url_test.cc ) -if(WEBCC_ENABLE_REST) - set(UT_SRCS ${UT_SRCS} - rest_service_manager_test.cc - ) -endif() - set(UT_TARGET_NAME webcc_unittest) add_executable(${UT_TARGET_NAME} ${UT_SRCS}) diff --git a/unittest/base64_test.cc b/unittest/base64_test.cc new file mode 100644 index 0000000..76ce22d --- /dev/null +++ b/unittest/base64_test.cc @@ -0,0 +1,29 @@ +#include "gtest/gtest.h" + +#include "webcc/base64.h" + +TEST(Base64, Encode) { + std::string encoded; + + encoded = webcc::Base64Encode("ban"); + EXPECT_EQ("YmFu", encoded); + + encoded = webcc::Base64Encode("bana"); + EXPECT_EQ("YmFuYQ==", encoded); + + encoded = webcc::Base64Encode("banan"); + EXPECT_EQ("YmFuYW4=", encoded); +} + +TEST(Base64, Decode) { + std::string decoded; + + decoded = webcc::Base64Decode("YmFu"); + EXPECT_EQ("ban", decoded); + + decoded = webcc::Base64Decode("YmFuYQ=="); + EXPECT_EQ("bana", decoded); + + decoded = webcc::Base64Decode("YmFuYW4="); + EXPECT_EQ("banan", decoded); +} diff --git a/unittest/rest_service_manager_test.cc b/unittest/rest_service_manager_test.cc index 54e1eda..a8f03a1 100644 --- a/unittest/rest_service_manager_test.cc +++ b/unittest/rest_service_manager_test.cc @@ -37,23 +37,6 @@ TEST(RestServiceManager, URL_RegexBasic) { EXPECT_FALSE(!!service); } -TEST(RestServiceManager, URL_Temp) { - webcc::RestServiceManager service_manager; - - service_manager.AddService(std::make_shared(), - "/instance/([\\w.]+)", true); - - std::vector matches; - - std::string url = "/instance/123.45-+6aaa"; - webcc::RestServicePtr service = service_manager.GetService(url, &matches); - - EXPECT_TRUE(!!service); - - EXPECT_EQ(1, matches.size()); - EXPECT_EQ("123.45-6aaa", matches[0]); -} - TEST(RestServiceManager, URL_RegexMultiple) { webcc::RestServiceManager service_manager; diff --git a/webcc/CMakeLists.txt b/webcc/CMakeLists.txt index 2f4cfa9..54114d6 100644 --- a/webcc/CMakeLists.txt +++ b/webcc/CMakeLists.txt @@ -14,6 +14,7 @@ configure_file( include(GNUInstallDirs) set(HEADERS + base64.h globals.h http_client.h http_client_pool.h @@ -42,6 +43,7 @@ set(HEADERS ) set(SOURCES + base64.cc globals.cc http_client.cc http_client_pool.cc diff --git a/webcc/base64.cc b/webcc/base64.cc new file mode 100644 index 0000000..16b9103 --- /dev/null +++ b/webcc/base64.cc @@ -0,0 +1,183 @@ +#include "webcc/base64.h" + +// Modified from Boost.Beast (boost\beast\core\detail\base64.hpp) +// See the comments below for the original authors and copyrights. + +// +// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/beast +// + +/* + Portions from http://www.adp-gmbh.ch/cpp/common/base64.html + Copyright notice: + + base64.cpp and base64.h + + Copyright (C) 2004-2008 Rene Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + Rene Nyffenegger rene.nyffenegger@adp-gmbh.ch +*/ + +namespace webcc { + +namespace base64 { + +// The Base 64 Alphabet. +static const char kAlphabet[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +// kInverse['z'] -> 51 which is the index of 'z' in kAlphabet. +static const char kInverse[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0-15 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16-31 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, // 32-47 + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 48-63 + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 64-79 + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 80-95 + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111 + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 112-127 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128-143 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144-159 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160-175 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176-191 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192-207 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208-223 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224-239 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240-255 +}; + +// Return max chars needed to encode a base64 string. +inline std::size_t constexpr EncodedSize(std::size_t n) { + return 4 * ((n + 2) / 3); +} + +// Return max bytes needed to decode a base64 string. +inline std::size_t constexpr DecodedSize(std::size_t n) { + // return 3 * n / 4; + return n / 4 * 3; // requires n&3==0, smaller +} + +std::size_t Encode(const void* src, std::size_t len, void* dst) { + char* out = static_cast(dst); + const char* in = static_cast(src); + + for (auto n = len / 3; n--;) { + *out++ = kAlphabet[ (in[0] & 0xfc) >> 2]; + *out++ = kAlphabet[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)]; + *out++ = kAlphabet[((in[1] & 0x0f) << 2) + ((in[2] & 0xc0) >> 6)]; + *out++ = kAlphabet[ in[2] & 0x3f]; + in += 3; + } + + switch (len % 3) { + case 2: + *out++ = kAlphabet[ (in[0] & 0xfc) >> 2]; + *out++ = kAlphabet[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)]; + *out++ = kAlphabet[ (in[1] & 0x0f) << 2]; + *out++ = '='; + break; + + case 1: + *out++ = kAlphabet[ (in[0] & 0xfc) >> 2]; + *out++ = kAlphabet[((in[0] & 0x03) << 4)]; + *out++ = '='; + *out++ = '='; + break; + + case 0: + break; + } + + return out - static_cast(dst); +} + +typedef std::pair SizePair; + +SizePair Decode(const char* src, std::size_t len, void* dst) { + auto in = reinterpret_cast(src); + unsigned char* out = static_cast(dst); + + char c4[4]; + unsigned char c3[3]; + int i = 0; + + while (len-- && *in != '=') { + char v = kInverse[*in]; + if(v == -1) { break; } + ++in; + c4[i] = v; + + if(++i == 4) { + c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4); + c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2); + c3[2] = ((c4[2] & 0x3) << 6) + c4[3]; + + for (i = 0; i < 3; i++) { + *out++ = c3[i]; + } + + i = 0; + } + } + + if (i > 0) { + c3[0] = ( c4[0] << 2) + ((c4[1] & 0x30) >> 4); + c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2); + c3[2] = ((c4[2] & 0x3) << 6) + c4[3]; + + for (int j = 0; j < i - 1; j++) { + *out++ = c3[j]; + } + } + + return {out - static_cast(dst), + in - reinterpret_cast(src)}; +} + +} // namespace base64 + +std::string Base64Encode(const std::uint8_t* data, std::size_t len) { + std::string dst; + dst.resize(base64::EncodedSize(len)); + dst.resize(base64::Encode(data, len, &dst[0])); + return dst; +} + +std::string Base64Encode(const std::string& s) { + return Base64Encode(reinterpret_cast(s.data()), s.size()); +} + +std::string Base64Decode(const std::string& data) { + std::string dst; + dst.resize(base64::DecodedSize(data.size())); + auto result = base64::Decode(data.data(), data.size(), &dst[0]); + dst.resize(result.first); + return dst; +} + +} // namespace webcc diff --git a/webcc/base64.h b/webcc/base64.h new file mode 100644 index 0000000..2145adb --- /dev/null +++ b/webcc/base64.h @@ -0,0 +1,17 @@ +#ifndef WEBCC_BASE64_H_ +#define WEBCC_BASE64_H_ + +#include +#include + +namespace webcc { + +std::string Base64Encode(const std::uint8_t* data, std::size_t len); + +std::string Base64Encode(const std::string& input); + +std::string Base64Decode(const std::string& data); + +} // namespace webcc + +#endif // WEBCC_BASE64_H_