[
  {
    "path": ".github/workflows/cmake.yml",
    "content": "name: Build\n\non:\n  push:\n    branches: [ \"master\" ]\n  pull_request:\n    branches: [ \"master\" ]\n\nenv:\n  BUILD_TYPE: Release\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v3\n    \n    - name: Checkout submodules\n      run: git submodule update --init --recursive\n\n    - name: Configure CMake\n      run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}\n\n    - name: Build\n      run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}\n"
  },
  {
    "path": ".gitignore",
    "content": "# Compiled Object files\n*.slo\n*.lo\n*.o\n*.obj\n\n# Compiled Dynamic libraries\n*.so\n*.dylib\n*.dll\n\n# Compiled Static libraries\n*.lai\n*.la\n*.a\n*.lib\n\n# Executables\n*.exe\n*.out\n*.app\n\n/.idea/\n/build/"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"third_party/jsonxx\"]\n\tpath = third_party/jsonxx\n\turl = https://github.com/hjiang/jsonxx.git\n[submodule \"third_party/networking-ts-impl\"]\n\tpath = third_party/networking-ts-impl\n\turl = https://github.com/chriskohlhoff/networking-ts-impl.git\n[submodule \"third_party/curi\"]\n\tpath = third_party/curi\n\turl = https://github.com/lxrite/curi.git\n[submodule \"third_party/mbedtls\"]\n\tpath = third_party/mbedtls\n\turl = https://github.com/Mbed-TLS/mbedtls.git\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 2.6)\nproject(azure-http-proxy)\n\nif(NOT CMAKE_BUILD_TYPE)\n  set(CMAKE_BUILD_TYPE Release)\nendif()\n\ninclude_directories(\"third_party/networking-ts-impl/include\")\n\nset(ENABLE_PROGRAMS OFF CACHE BOOL \"\" FORCE)\nset(ENABLE_TESTING OFF CACHE BOOL \"\" FORCE)\nset(MBEDTLS_CONFIG_FILE \"${CMAKE_CURRENT_SOURCE_DIR}/src/configs/ahp_mbedtls_config.h\" CACHE STRING \"\" FORCE)\nadd_subdirectory(\"third_party/mbedtls\")\nif(\"${CMAKE_CXX_COMPILER_ID}\" MATCHES \"GNU|Clang\")\n  target_compile_options(mbedcrypto PRIVATE \"-Wno-unreachable-code\")\nendif()\n\nif(\"${CMAKE_CXX_COMPILER_ID}\" MATCHES \"Clang\")\n  set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -std=c++17\")\nelseif(\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"GNU\")\n  set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -std=c++17\")\nelseif(\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"MSVC\")\n  add_compile_options(\"/std:c++17\")\nendif()\n\ninclude_directories(\"third_party/jsonxx\")\nadd_library(jsonxx STATIC third_party/jsonxx/jsonxx.cc)\n\ninclude_directories(\"third_party/curi/src\")\nadd_subdirectory(\"third_party/curi/src\")\n\nadd_executable(ahpc src/http_proxy_client_main.cpp src/http_proxy_client.cpp src/http_proxy_client_config.cpp src/http_proxy_client_connection.cpp src/hash_utils.cpp)\ntarget_link_libraries(ahpc mbedtls jsonxx)\n\nadd_executable(ahps src/http_proxy_server_main.cpp src/http_proxy_server.cpp src/http_proxy_server_config.cpp src/http_proxy_server_connection.cpp src/http_header_parser.cpp src/hash_utils.cpp src/authentication.cpp)\ntarget_link_libraries(ahps mbedtls jsonxx curi)\n\nif(UNIX)\n  target_link_libraries(ahpc pthread)\n  target_link_libraries(ahps pthread)\nendif()\n\nif(WIN32)\n  if(MINGW)\n    target_link_libraries(ahpc ws2_32 wsock32)\n    target_link_libraries(ahps ws2_32 wsock32)\n  endif()\nendif()\n\ninstall(TARGETS ahpc ahps DESTINATION bin)"
  },
  {
    "path": "Dockerfile",
    "content": "FROM alpine:3.18 as builder\n\nRUN apk update \\\n    && apk add alpine-sdk cmake linux-headers\n\nWORKDIR /ahp\n\nCOPY . .\n\nRUN cmake -B build -DCMAKE_BUILD_TYPE=Release \\\n    && cmake --build build\n\nFROM alpine:3.18\n\nRUN apk update && apk add libgcc libstdc++\n\nCOPY --from=builder /ahp/build/ahps /usr/local/bin/ahps\nCOPY --from=builder /ahp/build/ahpc /usr/local/bin/ahpc\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2013-2015 limhiaoing <blog.poxiao.me>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "README.md",
    "content": "# azure-http-proxy\n\n## 简介\n\nAHP(Azure Http Proxy)是一款高速、安全、轻量级和跨平台的HTTP代理，使用对称加密算法AES对传输的数据进行加密，使用非对称加密算法RSA传输密钥。\n\n## 特性\n - 一连接一密钥，AHP会对每个连接使用一个随机生成的密钥和初始化向量，避免重复使用同一密钥\n - 使用非对称加密算法RSA传输密钥，只需对客户端公开RSA公钥\n - 对目标域名的解析在服务端进行，可以解决本地DNS污染的问题\n - 服务端同时支持多种数据加密方式，数据加密方式可由客户端任意指定，客户端可以权衡机器性能以及安全需求选择合适的加密方式\n - 多线程并发处理，充分利用多处理器的优势，能同时处理成千上万的并发连接\n - 多用户支持，允许为每个用户使用独立的auth_key `(1.1及以上版本)`\n\n## 编译和安装\n\n### 拉取代码\n``` shell\n$ git clone --recursive https://github.com/lxrite/azure-http-proxy.git\n```\n\n### 编译器\n\nAHP使用了部分C++17特性，所以对编译器的版本有较高要求，下面列出了部分已测试过可以用来编译AHP的编译器\n\n - Microsoft Visual Studio >= 2017\n - GCC >= 7.3\n - Clang >= 6.0\n\n### 编译\nAHP使用自动化构建工具CMake来实现跨平台构建\n\n - CMake >= 2.8\n\n```shell\n$ cd azure-http-proxy\n$ mkdir build\n$ cd build\n$ cmake -DCMAKE_BUILD_TYPE=Release ..\n$ cmake --build .\n```\n\n如果编译成功会生成ahpc（客户端）和ahps（服务端）。\n\nOpenWrt/LEDE 编译参考 [openwrt-ahp](https://github.com/lxrite/openwrt-ahp)\n\n## 配置和运行\n\n完整的配置示例见这里： https://github.com/lxrite/azure-http-proxy/tree/master/example\n\n注意：不要使用示例配置中的RSA私钥和公钥，因为私钥一公开就是不安全的了。\n\n如果你要运行的是服务端，那么你首先需要生成一对RSA密钥对，AHP支持任意长度不小于1024位的RSA密钥。下面的命令使用openssl生成2048位的私钥和公钥\n```shell\n$ openssl genrsa -out rsa_private_key.pem 2048\n$ openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem\n```\n\n服务端保留私钥并将公钥告诉客户端。\n\n### 配置服务端 \n\n编辑`server.json`文件\n```json\n{\n  \"bind_address\": \"0.0.0.0\",\n  \"listen_port\": 8090,\n  \"rsa_private_key\": \"-----BEGIN RSA PRIVATE KEY----- ...... -----END RSA PRIVATE KEY-----\",\n  \"timeout\": 240,\n  \"workers\": 4,\n  \"auth\": true,\n  \"auth_key_list\": [\n    \"testing_key\",\n    \"Bob\",\n    \"Alice\"\n  ]\n}\n```\n\n字段名          | 描述               | 是否必选         | 默认值    |\n----------------|--------------------|------------------|-----------|\nbind_address    | 服务端绑定的IP地址 | 否               | \"0.0.0.0\" |\nlisten_port     | 服务端绑定的端口   | 否               | 8090      |\nrsa_private_key | RSA私钥          | 是               | 无        |\ntimeout         | 超时时间（秒）     | 否               | 240       |\nworkers         | 并发工作线程数     | 否               | 4         |\nauth            | 启用代理身份验证   | 否               | false     |\nauth_key_list   | auth_key列表     | auth为true时必选  | 无        |\n\n### 配置客户端\n\n编辑`client.json`文件\n```json\n{\n  \"proxy_server_address\": \"127.0.0.1\",\n  \"proxy_server_port\": 8090,\n  \"bind_address\": \"127.0.0.1\",\n  \"listen_port\": 8089,\n  \"rsa_public_key\": \"-----BEGIN PUBLIC KEY----- ...... -----END PUBLIC KEY-----\",\n  \"cipher\": \"aes-256-cfb\",\n  \"timeout\": 240,\n  \"workers\": 2,\n  \"auth_key\": \"testing_key\"\n}\n```\n\n字段名               | 描述                 | 是否必选         | 默认值        |\n---------------------|----------------------|------------------|---------------|\nproxy_server_address | 服务端的IP地址或域名 | 是               | 无            |\nproxy_server_port    | 服务端的端口         | 是               | 无            |\nbind_address         | 客户端绑定的IP地址   | 否               | \"127.0.0.1\"   |\nlisten_port          | 客户端的监听端口     | 否               | 8089          |\nrsa_public_key       | RSA公钥              | 是               | 无            |\ncipher               | 加密方法             | 否               | \"aes-256-cfb\" |\ntimeout              | 超时时间（秒）       | 否               | 240           |\nworkers              | 并发工作线程数       | 否               | 2             |\nauth_key             | 用于身份验证的字符串  | 否               | 值为空字符串或没有这个字段时，请求不携带auth_key，仅当server的auth为false时才能成功建立连接|\n\n#### 支持的加密方法\n\n - aes-xyz-cfb\n - <del>aes-xyz-cfb8</del> (自1.2版本开始不再支持)\n - <del>aes-xyz-cfb1</del> (自1.2版本开始不再支持)\n - aes-xyz-ofb\n - aes-xyz-ctr\n\n中间的xyz可以为128、192或256。\n\n## 运行\n\n确定配置无误后就可以运行AHP了。\n\n### 运行服务端\n\nLinux或其他类Unix系统\n```shell\n$ ./ahps -c server.json\n```\n\nWindows\n```shell\n$ ahps.exe -c server.json\n``` \n\n### 运行客户端\n\nLinux或其他类Unix系统\n```shell\n$ ./ahpc -c client.json\n```\n\nWindows\n```shell\n$ ahpc.exe -c client.json\n```\n\n## 使用Docker\n```shell\n# 拉取镜像\ndocker pull ghcr.io/lxrite/azure-http-proxy:latest\n\n# 启动 ahps\ndocker run -d -p 8090:8090 -v $PWD/server.json:/data/ahp/server.json ghcr.io/lxrite/azure-http-proxy ahps -c /data/ahp/server.json\n```\n"
  },
  {
    "path": "example/client.json",
    "content": "{\r\n    \"proxy_server_address\": \"127.0.0.1\",\r\n    \"proxy_server_port\": 8090,\r\n    \"bind_address\": \"127.0.0.1\",\r\n    \"listen_port\": 8089,\r\n    \"rsa_public_key\": \"-----BEGIN PUBLIC KEY-----\\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxPbPBU61RYBI0rUDVso+\\nTzkQ7bXO1j4GWxbYZ3nEL6sLbrftv7rpYq5uPLi9DdJ3ZoEUjxlnO+VUOOtm7LpR\\nGmWqUQdNnYHuiZU1UuH7pDIXejQwwSC698FB1kwnoxV4LICkiA1a4qucqlnG8nN6\\ngBFs3/1K2DuUs0Hg1hZKlkOq/ONR82XGhXkB/HVwmfgQlZpVbWHQDsZiOv1SUnQW\\n8Zs6E/JmW6llBkWtsQT9nQ2uzcV1JGzV0ltB4N0CMC8u2zv/LLTSgS4IKrVicAqO\\n9TWkGOFmGowV7PpEAEQC1WcBXThLpUYk2QqiSvTTLTdFNmwEH+hKa1ZBPqOcaTA1\\ndQIDAQAB\\n-----END PUBLIC KEY-----\",\r\n    \"cipher\": \"aes-256-cfb\",\r\n    \"timeout\": 240,\r\n    \"workers\": 2,\r\n    \"auth_key\": \"testing_key\"\r\n}\r\n"
  },
  {
    "path": "example/server.json",
    "content": "{\r\n    \"bind_address\": \"0.0.0.0\",\r\n    \"listen_port\": 8090,\r\n    \"rsa_private_key\": \"-----BEGIN RSA PRIVATE KEY-----\\nMIIEowIBAAKCAQEAxPbPBU61RYBI0rUDVso+TzkQ7bXO1j4GWxbYZ3nEL6sLbrft\\nv7rpYq5uPLi9DdJ3ZoEUjxlnO+VUOOtm7LpRGmWqUQdNnYHuiZU1UuH7pDIXejQw\\nwSC698FB1kwnoxV4LICkiA1a4qucqlnG8nN6gBFs3/1K2DuUs0Hg1hZKlkOq/ONR\\n82XGhXkB/HVwmfgQlZpVbWHQDsZiOv1SUnQW8Zs6E/JmW6llBkWtsQT9nQ2uzcV1\\nJGzV0ltB4N0CMC8u2zv/LLTSgS4IKrVicAqO9TWkGOFmGowV7PpEAEQC1WcBXThL\\npUYk2QqiSvTTLTdFNmwEH+hKa1ZBPqOcaTA1dQIDAQABAoIBAQC0FTSSlbQHJ5Nt\\nkzLKV39Az9w6es/x8iO9hNW3Rg2px6lcQa6ObkaUgwcnXYD88kFY5wv1CjTo/nRS\\n1mf0aSVeDTX7f8HnELUX9eQbM6LwLRxqDu3EpjhL7dZUKKzU3vxRNA06l3tRY7aJ\\nyiur4QlPrp7s7JlRuqfqfYEkPJ1VZE5CN/0chY21rD9nK30Y9+Rttq/oIW0NyK74\\nOJYgottIoiBRwgc2ZTxXalWq5yKWXBVZZlcTSOxH8lmDY5Z0md2oYfgTBqUUEzlz\\nLm9S88+ydBtUVXSlDN4PPh+N3arshFnRZBAxNtxiIGi4PYfYJmb2U6a1lq0nj12g\\nKs6Wk0QBAoGBAOiiJBdhy3iL/29Z7floQoa5iQgPxsSh8hv6jgIos7B2n/Nkre68\\ngK/ULw9utsj/lq6Lg9amMPv+hW4rbdL88UfQFFcvjixhgqk9RNNvt51Lg03AIM6w\\npF+FPWqQOisrQmPgxWnKNoIZsfEayBm/mTPHk6VBIVq7HmeF9hxMjcilAoGBANi/\\nes4zzfB7PmvuBJxLHe6YWIqNCk8aS/uZ4gZMGN9stLFOZPeeshhlq4OTu7m37MzD\\nD5RERDFA3y+OnpUnsB5nXwYqIslIPQ1H6WeNia/xusE45OthbBu6ZgpDI6TmTQ5S\\nAfLJMIaurkxSzi9jtotCtYVQ8iBvXIlVqNuAFlCRAoGAUepONSGNiTwazPXosysA\\nSfppAzqy7ihsXWfDu4TjiR6sQgNQr8EWu1NG4vNET9CYGYws91d75jAAggOu312M\\nBJKDpxOqx3vqi3d0ldM/35ZofOdLZhyTNMNxFeYFZZANB6htO0wmF3e+zbx1e4OZ\\nCb+cANPHT/CVyzRDrkFSp7UCgYAaPjzhQJZ7uoBZUw1N2y55mAqIZAFOiJGQmhYq\\nywXr716FZUeGT7miiJTWrol5OBK8zBVTz1wuVntvZ6Y9yvthAwUXWvyxQETcGcvh\\n1NpJ6kvBX2EgOsB7Lvtx3KUxLw0/YaCIw+FTPU0vQotiY4FTpTUTOjTGxpT+r9dt\\nFDofcQKBgCy5lnl7oAEK7sPNSxjyi1jELBcMl6bcE6sLkyXli7gpQn33NEFKEM16\\nSfKNttCJFKx+3LtJNjMZ6xKQsEGHAQTXu9MxDd+g16ws8W+tIeMLf+Hn+sI+UFND\\nfToIgs+zqOC67B/MVPDtRkJskhcUFRjqmcFGJIlkK9TiYHQm1ldW\\n-----END RSA PRIVATE KEY-----\",\r\n    \"timeout\": 240,\r\n    \"workers\": 4,\r\n    \"auth\": true,\r\n    \"auth_key_list\": [\r\n        \"testing_key\",\r\n        \"Bob\",\r\n        \"Alice\",\r\n        \"+ZI1w$u9N65lTw*nL@$\",\r\n        \"SoHPa4xNMBWoXxSOp+6snUtqtdXFH(MO\"\r\n    ]\r\n}\r\n"
  },
  {
    "path": "src/authentication.cpp",
    "content": "/*\r\n *    authentication.cpp:\r\n *\r\n *    Copyright (C) 2015-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n#include <algorithm>\r\n#include <iterator>\r\n\r\n#include \"authentication.hpp\"\r\n#include \"hash_utils.hpp\"\r\n\r\nnamespace azure_proxy {\r\n\r\nauthentication::authentication()\r\n{\r\n}\r\n\r\nbool authentication::auth(const auth_key_hash_t& auth_key_hash) const\r\n{\r\n    return this->auth_keys_map.find(auth_key_hash) != this->auth_keys_map.end();\r\n}\r\n\r\nvoid authentication::add_auth_key(const std::string& auth_key)\r\n{\r\n    auto auth_key_hash = hash_utils::sha256(reinterpret_cast<const unsigned char*>(auth_key.data()), auth_key.size());\r\n    this->auth_keys_map[auth_key_hash] = auth_key;\r\n}\r\n\r\nvoid authentication::remove_all_auth_keys()\r\n{\r\n    this->auth_keys_map.clear();\r\n}\r\n\r\nauthentication& authentication::get_instance()\r\n{\r\n    static authentication instance;\r\n    return instance;\r\n}\r\n\r\n} // namespace azure_proxy\r\n"
  },
  {
    "path": "src/authentication.hpp",
    "content": "/*\r\n *    authentication.hpp:\r\n *\r\n *    Copyright (C) 2015-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n#ifndef AZURE_AUTHENTICATION_HPP\r\n#define AZURE_AUTHENTICATION_HPP\r\n\r\n#include <array>\r\n#include <map>\r\n#include <string>\r\n\r\nnamespace azure_proxy {\r\n\r\nusing auth_key_hash_t = std::array<unsigned char, 32>;\r\n\r\nclass authentication {\r\n    std::map<auth_key_hash_t, std::string> auth_keys_map;\r\nprivate:\r\n    authentication();\r\npublic:\r\n    bool auth(const auth_key_hash_t& auth_key_hash) const;\r\n    void add_auth_key(const std::string& auth_key);\r\n    void remove_all_auth_keys();\r\n\r\n    static authentication& get_instance();\r\n};\r\n\r\n} // namespace azure_proxy\r\n\r\n#endif\r\n"
  },
  {
    "path": "src/configs/ahp_mbedtls_config.h",
    "content": "#define MBEDTLS_HAVE_TIME\n#define MBEDTLS_HAVE_TIME_DATE\n#define MBEDTLS_CIPHER_MODE_CFB\n#define MBEDTLS_CIPHER_MODE_CTR\n#define MBEDTLS_CIPHER_MODE_OFB\n#define MBEDTLS_ERROR_STRERROR_DUMMY\n#define MBEDTLS_AES_C\n#define MBEDTLS_ASN1_PARSE_C\n#define MBEDTLS_BASE64_C\n#define MBEDTLS_CIPHER_C\n#define MBEDTLS_ENTROPY_C\n#define MBEDTLS_PEM_PARSE_C\n#define MBEDTLS_PLATFORM_C\n#define MBEDTLS_SHA1_C\n#define MBEDTLS_SHA256_C\n#define MBEDTLS_TIMING_C\n#define MBEDTLS_VERSION_C\n#define MBEDTLS_MD_C\n#define MBEDTLS_OID_C\n#define MBEDTLS_BIGNUM_C\n#define MBEDTLS_RSA_C\n#define MBEDTLS_PK_C\n#define MBEDTLS_PK_PARSE_C\n#define MBEDTLS_PKCS1_V21\n#define MBEDTLS_CTR_DRBG_C\n"
  },
  {
    "path": "src/encrypt.hpp",
    "content": "/*\r\n *    encrypt.hpp:\r\n *\r\n *    Copyright (C) 2014-2024 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n#ifndef AZURE_ENCRYPT_HPP\r\n#define AZURE_ENCRYPT_HPP\r\n\r\n#include <cassert>\r\n#include <cstring>\r\n#include <iostream>\r\n#include <memory>\r\n#include <stdexcept>\r\n\r\n#include <mbedtls/cipher.h>\r\n#include <mbedtls/ctr_drbg.h>\r\n#include <mbedtls/entropy.h>\r\n#include <mbedtls/error.h>\r\n#include <mbedtls/pk.h>\r\n#include <mbedtls/rsa.h>\r\n\r\nnamespace azure_proxy {\r\n\r\nclass stream_encryptor {\r\npublic:\r\n    virtual void encrypt(const unsigned char* in, unsigned char* out, std::size_t length) = 0;\r\n    virtual ~stream_encryptor() {}\r\n};\r\n\r\nclass stream_decryptor {\r\npublic:\r\n    virtual void decrypt(const unsigned char* in, unsigned char* out, std::size_t length) = 0;\r\n    virtual ~stream_decryptor() {}\r\n};\r\n\r\nclass copy_encryptor : public stream_encryptor {\r\npublic:\r\n    copy_encryptor() {};\r\n    virtual void encrypt(const unsigned char* in, unsigned char* out, std::size_t length) {\r\n        assert(in && out);\r\n        std::memcpy(out, in, length);\r\n    }\r\n    virtual ~copy_encryptor() {}\r\n};\r\n\r\nclass copy_decryptor : public stream_decryptor {\r\npublic:\r\n    copy_decryptor() {};\r\n    virtual void decrypt(const unsigned char* in, unsigned char* out, std::size_t length) {\r\n        assert(in && out);\r\n        std::memcpy(out, in, length);\r\n    }\r\n    virtual ~copy_decryptor() {}\r\n};\r\n\r\nclass aes_stream_encryptor : public stream_encryptor {\r\n    mbedtls_cipher_context_t aes_ctx_;\r\npublic:\r\n    aes_stream_encryptor(const unsigned char* key, const mbedtls_cipher_info_t* cipher_info, unsigned char* ivec) {\r\n        assert(key && cipher_info && ivec);\r\n        mbedtls_cipher_init(&aes_ctx_);\r\n        std::unique_ptr<mbedtls_cipher_context_t, decltype(&mbedtls_cipher_free)> auto_free(&aes_ctx_, mbedtls_cipher_free);\r\n        int ret = mbedtls_cipher_setup(&aes_ctx_, cipher_info);\r\n        if (ret != 0) {\r\n            std::cerr << \"mbedtls_cipher_setup error: \" << ret << std::endl;\r\n            throw std::runtime_error(\"mbedtls_cipher_setup error\");\r\n        }\r\n        ret = mbedtls_cipher_setkey(&aes_ctx_, key, mbedtls_cipher_info_get_key_bitlen(cipher_info), MBEDTLS_ENCRYPT);\r\n        if (ret != 0) {\r\n            std::cerr << \"mbedtls_cipher_setkey error: \" << ret << std::endl;\r\n            throw std::runtime_error(\"mbedtls_cipher_setkey error\");\r\n        }\r\n        ret = mbedtls_cipher_set_iv(&aes_ctx_, ivec, mbedtls_cipher_info_get_iv_size(cipher_info));\r\n        if (ret != 0) {\r\n            std::cerr << \"mbedtls_cipher_set_iv error: \" << ret << std::endl;\r\n            throw std::runtime_error(\"mbedtls_cipher_set_iv error\");\r\n        }\r\n        auto_free.release();\r\n    }\r\n\r\n    virtual void encrypt(const unsigned char* in, unsigned char* out, std::size_t length) override {\r\n        assert(in && out);\r\n        std::size_t out_len = 0;\r\n        int ret = mbedtls_cipher_update(&aes_ctx_, in, length, out, &out_len);\r\n        if (ret != 0) {\r\n            std::cerr << \"mbedtls_cipher_update error: \" << ret << std::endl;\r\n            throw std::runtime_error(\"mbedtls_cipher_update error\");\r\n        }\r\n        assert(out_len == length);\r\n    }\r\n\r\n    virtual ~aes_stream_encryptor() override {\r\n        mbedtls_cipher_free(&aes_ctx_);\r\n    }\r\n};\r\n\r\nclass aes_stream_decryptor : public stream_decryptor {\r\n    mbedtls_cipher_context_t aes_ctx_;\r\npublic:\r\n    aes_stream_decryptor(const unsigned char* key, const mbedtls_cipher_info_t* cipher_info, unsigned char* ivec) {\r\n        assert(key && cipher_info && ivec);\r\n        mbedtls_cipher_init(&aes_ctx_);\r\n        std::unique_ptr<mbedtls_cipher_context_t, decltype(&mbedtls_cipher_free)> auto_free(&aes_ctx_, mbedtls_cipher_free);\r\n        int ret = mbedtls_cipher_setup(&aes_ctx_, cipher_info);\r\n        if (ret != 0) {\r\n            std::cerr << \"mbedtls_cipher_setup error: \" << ret << std::endl;\r\n            throw std::runtime_error(\"mbedtls_cipher_setup error\");\r\n        }\r\n        ret = mbedtls_cipher_setkey(&aes_ctx_, key, mbedtls_cipher_info_get_key_bitlen(cipher_info), MBEDTLS_DECRYPT);\r\n        if (ret != 0) {\r\n            std::cerr << \"mbedtls_cipher_setkey error: \" << ret << std::endl;\r\n            throw std::runtime_error(\"mbedtls_cipher_setkey error\");\r\n        }\r\n        ret = mbedtls_cipher_set_iv(&aes_ctx_, ivec, mbedtls_cipher_info_get_iv_size(cipher_info));\r\n        if (ret != 0) {\r\n            std::cerr << \"mbedtls_cipher_set_iv error: \" << ret << std::endl;\r\n            throw std::runtime_error(\"mbedtls_cipher_set_iv error\");\r\n        }\r\n        auto_free.release();\r\n    }\r\n\r\n    virtual void decrypt(const unsigned char* in, unsigned char* out, std::size_t length) override {\r\n        assert(in && out);\r\n        std::size_t out_len = 0;\r\n        int ret = mbedtls_cipher_update(&aes_ctx_, in, length, out, &out_len);\r\n        if (ret != 0) {\r\n            std::cerr << \"mbedtls_cipher_update error: \" << ret << std::endl;\r\n            throw std::runtime_error(\"mbedtls_cipher_update error\");\r\n        }\r\n        assert(out_len == length);\r\n    }\r\n\r\n    virtual ~aes_stream_decryptor() override {\r\n        mbedtls_cipher_free(&aes_ctx_);\r\n    }\r\n};\r\n\r\nstatic const mbedtls_cipher_info_t* aes_cfb128_cipher(std::size_t key_bits) {\r\n    assert(key_bits == 128 || key_bits == 192 || key_bits == 256);\r\n    mbedtls_cipher_type_t cipher_type;\r\n    switch (key_bits) {\r\n        case 128:\r\n            cipher_type = mbedtls_cipher_type_t::MBEDTLS_CIPHER_AES_128_CFB128;\r\n            break;\r\n        case 192:\r\n            cipher_type = mbedtls_cipher_type_t::MBEDTLS_CIPHER_AES_192_CFB128;\r\n            break;\r\n        default:\r\n            cipher_type = mbedtls_cipher_type_t::MBEDTLS_CIPHER_AES_256_CFB128;\r\n    }\r\n    return mbedtls_cipher_info_from_type(cipher_type);\r\n}\r\n\r\nstatic const mbedtls_cipher_info_t* aes_ofb128_cipher(std::size_t key_bits) {\r\n    assert(key_bits == 128 || key_bits == 192 || key_bits == 256);\r\n    mbedtls_cipher_type_t cipher_type;\r\n    switch (key_bits) {\r\n        case 128:\r\n            cipher_type = mbedtls_cipher_type_t::MBEDTLS_CIPHER_AES_128_OFB;\r\n            break;\r\n        case 192:\r\n            cipher_type = mbedtls_cipher_type_t::MBEDTLS_CIPHER_AES_192_OFB;\r\n            break;\r\n        default:\r\n            cipher_type = mbedtls_cipher_type_t::MBEDTLS_CIPHER_AES_256_OFB;\r\n    }\r\n    return mbedtls_cipher_info_from_type(cipher_type);\r\n}\r\n\r\nstatic const mbedtls_cipher_info_t* aes_ctr128_cipher(std::size_t key_bits) {\r\n    assert(key_bits == 128 || key_bits == 192 || key_bits == 256);\r\n    mbedtls_cipher_type_t cipher_type;\r\n    switch (key_bits) {\r\n        case 128:\r\n            cipher_type = mbedtls_cipher_type_t::MBEDTLS_CIPHER_AES_128_CTR;\r\n            break;\r\n        case 192:\r\n            cipher_type = mbedtls_cipher_type_t::MBEDTLS_CIPHER_AES_192_CTR;\r\n            break;\r\n        default:\r\n            cipher_type = mbedtls_cipher_type_t::MBEDTLS_CIPHER_AES_256_CTR;\r\n    }\r\n    return mbedtls_cipher_info_from_type(cipher_type);\r\n}\r\n\r\nclass aes_cfb128_encryptor : public aes_stream_encryptor {\r\npublic:\r\n    aes_cfb128_encryptor(const unsigned char* key, std::size_t key_bits, unsigned char* ivec) :\r\n        aes_stream_encryptor(key, aes_cfb128_cipher(key_bits), ivec) {\r\n    }\r\n};\r\n\r\nclass aes_cfb128_decryptor : public aes_stream_decryptor {\r\npublic:\r\n    aes_cfb128_decryptor(const unsigned char* key, std::size_t key_bits, unsigned char* ivec) :\r\n        aes_stream_decryptor(key, aes_cfb128_cipher(key_bits), ivec) {\r\n    }\r\n};\r\n\r\nclass aes_ofb128_encryptor : public aes_stream_encryptor {\r\npublic:\r\n    aes_ofb128_encryptor(const unsigned char* key, std::size_t key_bits, unsigned char* ivec) :\r\n        aes_stream_encryptor(key, aes_ofb128_cipher(key_bits), ivec) {\r\n    }\r\n};\r\n\r\nclass aes_ofb128_decryptor : public aes_stream_decryptor {\r\npublic:\r\n    aes_ofb128_decryptor(const unsigned char* key, std::size_t key_bits, unsigned char* ivec) :\r\n        aes_stream_decryptor(key, aes_ofb128_cipher(key_bits), ivec) {\r\n    }\r\n};\r\n\r\nclass aes_ctr128_encryptor : public aes_stream_encryptor {\r\npublic:\r\n    aes_ctr128_encryptor(const unsigned char* key, std::size_t key_bits, unsigned char* ivec) :\r\n        aes_stream_encryptor(key, aes_ctr128_cipher(key_bits), ivec) {\r\n    }\r\n};\r\n\r\nclass aes_ctr128_decryptor : public aes_stream_decryptor {\r\npublic:\r\n    aes_ctr128_decryptor(const unsigned char* key, std::size_t key_bits, unsigned char* ivec) :\r\n        aes_stream_decryptor(key, aes_ctr128_cipher(key_bits), ivec) {\r\n    }\r\n};\r\n\r\nclass rsa {\r\n    bool is_pub_key_;\r\n    mbedtls_pk_context pk_;\r\npublic:\r\n    rsa(const std::string& key) {\r\n        if (key.size() > 26 && std::equal(key.begin(), key.begin() + 26, \"-----BEGIN PUBLIC KEY-----\")) {\r\n            is_pub_key_ = true;\r\n        }\r\n        else if (key.size() > 31 && std::equal(key.begin(), key.begin() + 31, \"-----BEGIN RSA PRIVATE KEY-----\")) {\r\n            is_pub_key_ = false;\r\n        }\r\n        else {\r\n            throw std::invalid_argument(\"invalid RSA key\");\r\n        }\r\n\r\n        mbedtls_pk_init(&pk_);\r\n        std::unique_ptr<mbedtls_pk_context, decltype(&mbedtls_pk_free)> auto_free_pk(&pk_, mbedtls_pk_free);\r\n        int ret = 0;\r\n        if (is_pub_key_) {\r\n            ret = mbedtls_pk_parse_public_key(&pk_, reinterpret_cast<const unsigned char*>(key.c_str()), key.size() + 1);\r\n        } else {\r\n            mbedtls_entropy_context entropy;\r\n            mbedtls_ctr_drbg_context ctr_drbg;\r\n            mbedtls_entropy_init(&entropy);\r\n            mbedtls_ctr_drbg_init(&ctr_drbg);\r\n            std::unique_ptr<mbedtls_entropy_context, decltype(&mbedtls_entropy_free)> auto_free_entropy(&entropy, mbedtls_entropy_free);\r\n            std::unique_ptr<mbedtls_ctr_drbg_context, decltype(&mbedtls_ctr_drbg_free)> auto_free_ctr_drbg(&ctr_drbg, mbedtls_ctr_drbg_free);\r\n            int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, nullptr, 0);\r\n            if (ret != 0) {\r\n                std::cerr << \"mbedtls_ctr_drbg_seed error: \" << ret << std::endl;\r\n                throw std::runtime_error(\"mbedtls_ctr_drbg_seed error\");\r\n            }\r\n            ret = mbedtls_pk_parse_key(&pk_, reinterpret_cast<const unsigned char*>(key.c_str()), key.size() + 1, nullptr, 0, mbedtls_ctr_drbg_random, &ctr_drbg);\r\n        }\r\n        if (ret != 0) {\r\n            if (is_pub_key_) {\r\n                std::cerr << \"mbedtls_pk_parse_public_key error: \" << ret << std::endl;\r\n            } else {\r\n                std::cerr << \"mbedtls_pk_parse_key error: \" << ret << std::endl;\r\n            }\r\n            throw std::invalid_argument(\"invalid RSA key\");\r\n        }\r\n        auto pk_type = mbedtls_pk_get_type(&pk_);\r\n        if (pk_type != MBEDTLS_PK_RSA) {\r\n            std::cerr << \"mbedtls_pk_get_type: \" << pk_type << std::endl;\r\n            throw std::invalid_argument(\"invalid RSA key\");\r\n        }\r\n\r\n        ret = mbedtls_rsa_set_padding(mbedtls_pk_rsa(pk_), MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA1);\r\n        if (ret != 0) {\r\n            mbedtls_pk_free(&pk_);\r\n            std::cerr << \"mbedtls_rsa_set_padding error: \" << ret << std::endl;\r\n            throw std::runtime_error(\"mbedtls_rsa_set_padding error\");\r\n        }\r\n        auto_free_pk.release();\r\n    }\r\n\r\n    ~rsa() {\r\n        mbedtls_pk_free(&pk_);\r\n    }\r\n\r\n    int encrypt(int flen, unsigned char* from, unsigned char* to) {\r\n        assert(from && to);\r\n        mbedtls_entropy_context entropy;\r\n        mbedtls_ctr_drbg_context ctr_drbg;\r\n\r\n        mbedtls_entropy_init(&entropy);\r\n        mbedtls_ctr_drbg_init(&ctr_drbg);\r\n        std::unique_ptr<mbedtls_entropy_context, decltype(&mbedtls_entropy_free)> auto_free_entropy(&entropy, mbedtls_entropy_free);\r\n        std::unique_ptr<mbedtls_ctr_drbg_context, decltype(&mbedtls_ctr_drbg_free)> auto_free_ctr_drbg(&ctr_drbg, mbedtls_ctr_drbg_free);\r\n\r\n        int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, nullptr, 0);\r\n        if (ret != 0) {\r\n            std::cerr << \"mbedtls_ctr_drbg_seed error: \" << ret << std::endl;\r\n            return 0;\r\n        }\r\n\r\n        ret = mbedtls_rsa_pkcs1_encrypt(mbedtls_pk_rsa(pk_), mbedtls_ctr_drbg_random, &ctr_drbg, flen, from, to);\r\n        if (ret != 0) {\r\n            std::cerr << \"mbedtls_rsa_pkcs1_encrypt error: \" << ret << std::endl;\r\n            return 0;\r\n        }\r\n        return modulus_size();\r\n    }\r\n\r\n    int decrypt(int flen, unsigned char* from, unsigned char* to) {\r\n        assert(from && to);\r\n        mbedtls_entropy_context entropy;\r\n        mbedtls_ctr_drbg_context ctr_drbg;\r\n\r\n        mbedtls_entropy_init(&entropy);\r\n        mbedtls_ctr_drbg_init(&ctr_drbg);\r\n        std::unique_ptr<mbedtls_entropy_context, decltype(&mbedtls_entropy_free)> auto_free_entropy(&entropy, mbedtls_entropy_free);\r\n        std::unique_ptr<mbedtls_ctr_drbg_context, decltype(&mbedtls_ctr_drbg_free)> auto_free_ctr_drbg(&ctr_drbg, mbedtls_ctr_drbg_free);\r\n\r\n        int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, nullptr, 0);\r\n        if (ret != 0) {\r\n            std::cerr << \"mbedtls_ctr_drbg_seed error: \" << ret << std::endl;\r\n            return 0;\r\n        }\r\n\r\n        std::size_t out_len = 0;\r\n        ret = mbedtls_rsa_pkcs1_decrypt(mbedtls_pk_rsa(pk_), mbedtls_ctr_drbg_random, &ctr_drbg, &out_len, from, to, modulus_size());\r\n        if (ret != 0) {\r\n            std::cerr << \"mbedtls_rsa_pkcs1_decrypt error: \" << ret << std::endl;\r\n            return 0;\r\n        }\r\n        return static_cast<int>(out_len);\r\n    }\r\n\r\n    int modulus_size() const {\r\n        return mbedtls_pk_get_bitlen(&pk_) / 8;\r\n    }\r\n};\r\n\r\n} // namespace azure_proxy\r\n\r\n#endif\r\n"
  },
  {
    "path": "src/hash_utils.cpp",
    "content": "/*\n *    hash_utils.cpp:\n *\n *    Copyright (C) 2023-2024 Light Lin <lxrite@gmail.com> All Rights Reserved.\n *\n */\n\n#include \"hash_utils.hpp\"\n#include \"mbedtls/sha256.h\"\n\nnamespace azure_proxy {\nnamespace hash_utils {\n\nstd::array<unsigned char, 32> sha256(const unsigned char* data, std::size_t count) {\n    std::array<unsigned char, 32> result;\n    mbedtls_sha256_context ctx;\n    mbedtls_sha256_init(&ctx);\n    mbedtls_sha256_starts(&ctx, 0);\n    mbedtls_sha256_update(&ctx, data, count);\n    mbedtls_sha256_finish(&ctx, result.data());\n    mbedtls_sha256_free(&ctx);\n    return result;\n}\n\n} // namespace hash_utils\n} // namespace azure_proxy\n"
  },
  {
    "path": "src/hash_utils.hpp",
    "content": "/*\n *    hash_utils.hpp:\n *\n *    Copyright (C) 2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\n *\n */\n\n#ifndef AZURE_HASH_UTILS_HPP\n#define AZURE_HASH_UTILS_HPP\n\n#include <array>\n\nnamespace azure_proxy {\nnamespace hash_utils {\n\nstd::array<unsigned char, 32> sha256(const unsigned char* data, std::size_t count);\n\n} // namespace hash_utils\n} // namespace azure_proxy\n\n#endif // AZURE_HASH_UTILS_HPP\n"
  },
  {
    "path": "src/http_chunk_checker.hpp",
    "content": "﻿/*\r\n *    http_chunk_checker.hpp:\r\n *\r\n *    Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n#ifndef AZURE_HTTP_CHUNCK_CHCKER_HPP\r\n#define AZURE_HTTP_CHUNCK_CHCKER_HPP\r\n\r\n#include <cassert>\r\n#include <cctype>\r\n#include <iterator>\r\n#include <type_traits>\r\n\r\nnamespace azure_proxy {\r\n\r\nenum class http_chunk_check_state {\r\n    chunk_size_start,\r\n    chunk_size,\r\n    chunk_ext,\r\n    chunk_size_cr,\r\n    chunk_data,\r\n    chunk_data_cr,\r\n    chunk_last,\r\n    chunk_last_cr,\r\n    chunk_complete,\r\n    chunk_check_failed\r\n};\r\n\r\nclass http_chunk_checker {\r\n    http_chunk_check_state state;\r\n    std::uint32_t current_chunk_size;\r\n    std::uint32_t current_chunk_size_has_read;\r\npublic:\r\n    http_chunk_checker() : state(http_chunk_check_state::chunk_size_start), current_chunk_size(0), current_chunk_size_has_read(0) {}\r\n\r\n    bool is_complete() const {\r\n        return this->state == http_chunk_check_state::chunk_complete;\r\n    }\r\n\r\n    bool is_fail() const {\r\n        return this->state == http_chunk_check_state::chunk_check_failed;\r\n    }\r\n\r\n    template <typename ForwardIterator>\r\n    bool check(ForwardIterator begin, ForwardIterator end) {\r\n        static_assert(std::is_same<char, typename std::iterator_traits<ForwardIterator>::value_type>::value ||\r\n            std::is_same<signed char, typename std::iterator_traits<ForwardIterator>::value_type>::value ||\r\n            std::is_same<unsigned char, typename std::iterator_traits<ForwardIterator>::value_type>::value, \"error\");\r\n        assert(!this->is_fail());\r\n\r\n        for (auto iter = begin; iter != end; ++iter) {\r\n            switch (this->state) {\r\n                case http_chunk_check_state::chunk_size_start:\r\n                    if (std::isxdigit(static_cast<unsigned char>(*iter))) {\r\n                        this->current_chunk_size = (*iter) >= 'A' ? std::toupper(static_cast<unsigned char>(*iter)) - 'A' + 10 : *iter - '0';\r\n                        this->current_chunk_size_has_read = 0;\r\n                        this->state = http_chunk_check_state::chunk_size;\r\n                        continue;\r\n                    }\r\n                    break;\r\n                case http_chunk_check_state::chunk_size:\r\n                    if (std::isxdigit(static_cast<unsigned char>(*iter))) {\r\n                        this->current_chunk_size = this->current_chunk_size * 16 + ((*iter) >= 'A' ? std::toupper(static_cast<unsigned char>(*iter)) - 'A' + 10 : *iter - '0');\r\n                        continue;\r\n                    }\r\n                    else if (*iter == ';' || *iter == ' ') {\r\n                        this->state = http_chunk_check_state::chunk_ext;\r\n                        continue;\r\n                    }\r\n                    else if (*iter == '\\r') {\r\n                        this->state = http_chunk_check_state::chunk_size_cr;\r\n                        continue;\r\n                    }\r\n                    break;\r\n                case http_chunk_check_state::chunk_ext:\r\n                    if (*iter == '\\r') {\r\n                        this->state = http_chunk_check_state::chunk_size_cr;\r\n                    }\r\n                    continue;\r\n                case http_chunk_check_state::chunk_size_cr:\r\n                    if (*iter == '\\n') {\r\n                        if (this->current_chunk_size == 0) {\r\n                            this->state = http_chunk_check_state::chunk_last;\r\n                        }\r\n                        else {\r\n                            this->state = http_chunk_check_state::chunk_data;\r\n                        }\r\n                        continue;\r\n                    }\r\n                    break;\r\n                case http_chunk_check_state::chunk_data:\r\n                    if (this->current_chunk_size_has_read < this->current_chunk_size) {\r\n                        ++this->current_chunk_size_has_read;\r\n                        continue;\r\n                    }\r\n                    else {\r\n                        if (*iter == '\\r') {\r\n                            this->state = http_chunk_check_state::chunk_data_cr;\r\n                            continue;\r\n                        }\r\n                    }\r\n                    break;\r\n                case http_chunk_check_state::chunk_data_cr:\r\n                    if (*iter == '\\n') {\r\n                        this->state = http_chunk_check_state::chunk_size_start;\r\n                        continue;\r\n                    }\r\n                    break;\r\n                case http_chunk_check_state::chunk_last:\r\n                    if (*iter == '\\r') {\r\n                        this->state = http_chunk_check_state::chunk_last_cr;\r\n                        continue;\r\n                    }\r\n                    break;\r\n                case http_chunk_check_state::chunk_last_cr:\r\n                    if (*iter == '\\n') {\r\n                        this->state = http_chunk_check_state::chunk_complete;\r\n                        continue;\r\n                    }\r\n                    break;\r\n                case http_chunk_check_state::chunk_complete:\r\n                    break;\r\n                default:\r\n                    assert(false);\r\n                    break;\r\n            }\r\n            this->state = http_chunk_check_state::chunk_check_failed;\r\n            return false;\r\n        }\r\n        return true;\r\n    }\r\n};\r\n\r\n} // namespace azure_proxy\r\n\r\n#endif\r\n"
  },
  {
    "path": "src/http_header_parser.cpp",
    "content": "/*\r\n *    http_header_parser.cpp:\r\n *\r\n *    Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n#include <cassert>\r\n#include <iterator>\r\n#include <regex>\r\n\r\n#include \"http_header_parser.hpp\"\r\n#include <curi.h>\r\n\r\nnamespace azure_proxy {\r\n\r\nhttp_request_header::http_request_header() : _port(80)\r\n{\r\n}\r\n\r\nconst std::string& http_request_header::method() const\r\n{\r\n    return this->_method;\r\n}\r\n\r\nconst std::string& http_request_header::scheme() const\r\n{\r\n    return this->_scheme;\r\n}\r\n\r\nconst std::string& http_request_header::host() const\r\n{\r\n    return this->_host;\r\n}\r\n\r\nunsigned short http_request_header::port() const\r\n{\r\n    return this->_port;\r\n}\r\n\r\nconst std::string& http_request_header::path_and_query() const\r\n{\r\n    return this->_path_and_query;\r\n}\r\n\r\nconst std::string& http_request_header::http_version() const\r\n{\r\n    return this->_http_version;\r\n}\r\n\r\nstd::optional<std::string> http_request_header::get_header_value(const std::string& name) const\r\n{\r\n    auto iter = this->_headers_map.find(name);\r\n    if (iter == this->_headers_map.end()) {\r\n        return std::nullopt;\r\n    }\r\n    return std::get<1>(*iter);\r\n}\r\n\r\nstd::size_t http_request_header::erase_header(const std::string& name)\r\n{\r\n    return this->_headers_map.erase(name);\r\n}\r\n\r\nconst http_headers_container& http_request_header::get_headers_map() const\r\n{\r\n    return this->_headers_map;\r\n}\r\n\r\nhttp_response_header::http_response_header()\r\n{\r\n}\r\n\r\nconst std::string& http_response_header::http_version() const\r\n{\r\n    return this->_http_version;\r\n}\r\n\r\nunsigned int http_response_header::status_code() const\r\n{\r\n    return this->_status_code;\r\n}\r\n\r\nconst std::string& http_response_header::status_description() const\r\n{\r\n    return this->_status_description;\r\n}\r\n\r\nstd::optional<std::string> http_response_header::get_header_value(const std::string& name) const\r\n{\r\n    auto iter = this->_headers_map.find(name);\r\n    if (iter == this->_headers_map.end()) {\r\n        return std::nullopt;\r\n    }\r\n    return std::get<1>(*iter);\r\n}\r\n\r\nstd::size_t http_response_header::erase_header(const std::string& name)\r\n{\r\n    return this->_headers_map.erase(name);\r\n}\r\n\r\nconst http_headers_container& http_response_header::get_headers_map() const\r\n{\r\n    return this->_headers_map;\r\n}\r\n\r\nhttp_headers_container http_header_parser::parse_headers(std::string::const_iterator begin, std::string::const_iterator end)\r\n{\r\n    http_headers_container headers;\r\n\r\n    auto is_digit = [](char ch) -> bool {\r\n        return '0' <= ch && ch <= '9';\r\n    };\r\n    auto is_alpha = [](char ch) -> bool {\r\n        return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z');\r\n    };\r\n    auto is_token_char = [&is_alpha, &is_digit](char ch) -> bool {\r\n        return is_alpha(ch) || is_digit(ch) || (\r\n            ch == '!' || ch == '#' ||\r\n            ch == '$' || ch == '%' ||\r\n            ch == '&' || ch == '\\'' ||\r\n            ch == '`' || ch == '*' ||\r\n            ch == '+' || ch == '-' ||\r\n            ch == '.' || ch == '^' ||\r\n            ch == '_' || ch == '|' ||\r\n            ch == '~');\r\n    };\r\n\r\n    enum class parse_header_state {\r\n        header_field_name_start,\r\n        header_field_name,\r\n        header_field_value_left_ows,\r\n        header_field_value,\r\n        header_field_cr,\r\n        header_field_crlf,\r\n        header_field_crlfcr,\r\n        header_compelete,\r\n        header_parse_failed\r\n    };\r\n\r\n    parse_header_state state = parse_header_state::header_field_name_start;\r\n    std::string header_field_name;\r\n    std::string header_field_value;\r\n    for (std::string::const_iterator iter = begin; iter != end && state != parse_header_state::header_compelete && state != parse_header_state::header_parse_failed; ++iter) {\r\n        switch (state) {\r\n            case parse_header_state::header_field_name_start:\r\n                if (is_token_char(*iter)) {\r\n                    header_field_name.push_back(*iter);\r\n                    state = parse_header_state::header_field_name;\r\n                }\r\n                else if (iter == begin && *iter == '\\r') {\r\n                    state = parse_header_state::header_field_crlfcr;\r\n                }\r\n                else {\r\n                    state = parse_header_state::header_parse_failed;\r\n                }\r\n                break;\r\n            case parse_header_state::header_field_name:\r\n                if (is_token_char(*iter) || *iter == ' ') {\r\n                    header_field_name.push_back(*iter);\r\n                }\r\n                else if (*iter == ':') {\r\n                    state = parse_header_state::header_field_value_left_ows;\r\n                }\r\n                else {\r\n                    state = parse_header_state::header_parse_failed;\r\n                }\r\n                break;\r\n            case parse_header_state::header_field_value_left_ows:\r\n                if (*iter == ' ' || *iter == '\\t') {\r\n                    continue;\r\n                }\r\n                else if (*iter == '\\r') {\r\n                    state = parse_header_state::header_field_cr;\r\n                }\r\n                else {\r\n                    header_field_value.push_back(*iter);\r\n                    state = parse_header_state::header_field_value;\r\n                }\r\n                break;\r\n            case parse_header_state::header_field_value:\r\n                if (*iter == '\\r') {\r\n                    state = parse_header_state::header_field_cr;\r\n                }\r\n                else {\r\n                    header_field_value.push_back(*iter);\r\n                }\r\n                break;\r\n            case parse_header_state::header_field_cr:\r\n                if (*iter == '\\n') {\r\n                    state = parse_header_state::header_field_crlf;\r\n                }\r\n                else {\r\n                    state = parse_header_state::header_parse_failed;\r\n                }\r\n                break;\r\n            case parse_header_state::header_field_crlf:\r\n                if (*iter == ' ' || *iter == '\\t') {\r\n                    header_field_value.push_back(*iter);\r\n                    state = parse_header_state::header_field_value;\r\n                }\r\n                else {\r\n                    while (!header_field_name.empty() && (header_field_name[header_field_name.size() - 1] == ' ')) {\r\n                        header_field_name.resize(header_field_name.size() - 1);\r\n                    }\r\n                    assert(!header_field_name.empty());\r\n                    while (!header_field_value.empty() && (header_field_value[header_field_value.size() - 1] == ' ' || (header_field_value[header_field_value.size() - 1] == '\\t'))) {\r\n                        header_field_value.resize(header_field_value.size() - 1);\r\n                    }\r\n                    headers.insert(std::make_pair(std::move(header_field_name), std::move(header_field_value)));\r\n                    if (*iter == '\\r') {\r\n                        state = parse_header_state::header_field_crlfcr;\r\n                    }\r\n                    else if (is_token_char(*iter)) {\r\n                        header_field_name.push_back(*iter);\r\n                        state = parse_header_state::header_field_name;\r\n                    }\r\n                    else {\r\n                        state = parse_header_state::header_parse_failed;\r\n                    }\r\n                }\r\n                break;\r\n            case parse_header_state::header_field_crlfcr:\r\n                if (*iter == '\\n') {\r\n                    state = parse_header_state::header_compelete;\r\n                }\r\n                break;\r\n            default:\r\n                assert(false);\r\n        }\r\n    }\r\n    if (state != parse_header_state::header_compelete) {\r\n        throw std::runtime_error(\"failed to parse\");\r\n    }\r\n    return headers;\r\n}\r\n\r\nstd::optional<http_request_header> http_header_parser::parse_request_header(std::string::const_iterator begin, std::string::const_iterator end)\r\n{\r\n    auto iter = begin;\r\n    auto tmp = iter;\r\n    for (;iter != end && *iter != ' ' && *iter != '\\r'; ++iter)\r\n        ;\r\n    if (iter == tmp || iter == end || *iter != ' ') return std::nullopt;\r\n    http_request_header header;\r\n    header._method = std::string(tmp, iter);\r\n    tmp = ++iter;\r\n    for (;iter != end && *iter != ' ' && *iter != '\\r'; ++iter)\r\n        ;\r\n    if (iter == tmp || iter == end || *iter != ' ') return std::nullopt;\r\n    auto request_uri = std::string(tmp, iter);\r\n    if (header.method() == \"CONNECT\") {\r\n        std::regex regex(\"(.+?):(\\\\d+)\");\r\n        std::match_results<std::string::iterator> match_results;\r\n        if (!std::regex_match(request_uri.begin(), request_uri.end(), match_results, regex)) {\r\n            return std::nullopt;\r\n        }\r\n        header._host = match_results[1];\r\n        try {\r\n            header._port = static_cast<unsigned short>(std::stoul(std::string(match_results[2])));\r\n        }\r\n        catch (const std::exception&) {\r\n            return std::nullopt;\r\n        }\r\n    }\r\n    else {\r\n        struct parse_user_data {\r\n            std::string scheme;\r\n            std::string host;\r\n            unsigned int port = 80;\r\n            std::string path;\r\n            std::string query;\r\n        };\r\n        auto user_data = parse_user_data{};\r\n\r\n        curi_settings settings;\r\n        curi_default_settings(&settings);\r\n        settings.scheme_callback = [](void* user_data, const char* scheme, size_t scheme_len) -> int {\r\n            reinterpret_cast<parse_user_data*>(user_data)->scheme = std::string(scheme, scheme_len);\r\n            return 1;\r\n        };\r\n        settings.host_callback = [](void* user_data, const char* host, size_t host_len) -> int {\r\n            reinterpret_cast<parse_user_data*>(user_data)->host = std::string(host, host_len);\r\n            return 1;\r\n        };\r\n        settings.port_callback = [](void* user_data, unsigned int port) -> int {\r\n            reinterpret_cast<parse_user_data*>(user_data)->port = port;\r\n            return 1;\r\n        };\r\n        settings.path_callback = [](void* user_data, const char* path, size_t path_len) -> int {\r\n            reinterpret_cast<parse_user_data*>(user_data)->path = std::string(path, path_len);\r\n            return 1;\r\n        };\r\n        settings.query_callback = [](void* user_data, const char* query, size_t query_len) -> int {\r\n            reinterpret_cast<parse_user_data*>(user_data)->query = std::string(query, query_len);\r\n            return 1;\r\n        };\r\n        if (curi_status_success != curi_parse_full_uri(request_uri.c_str(), request_uri.size(), &settings, &user_data)) {\r\n            return std::nullopt;\r\n        }\r\n\r\n        if (user_data.scheme.empty() || user_data.host.empty() || user_data.path.empty()) {\r\n            return std::nullopt;\r\n        }\r\n        header._scheme = user_data.scheme;\r\n        header._host = user_data.host;\r\n        header._port = user_data.port;\r\n        header._path_and_query = user_data.path;\r\n        if (!user_data.query.empty()) {\r\n            header._path_and_query.push_back('?');\r\n            header._path_and_query += user_data.query;\r\n        }\r\n    }\r\n\r\n    tmp = ++iter;\r\n    for (;iter != end && *iter != '\\r'; ++iter)\r\n        ;\r\n    // HTTP/x.y\r\n    if (iter == end || std::distance(tmp, iter) < 6 || !std::equal(tmp, tmp + 5, \"HTTP/\")) return std::nullopt;\r\n\r\n    header._http_version = std::string(tmp + 5, iter);\r\n\r\n    ++iter;\r\n    if (iter == end || *iter != '\\n') return std::nullopt;\r\n\r\n    ++iter;\r\n    try {\r\n        header._headers_map = parse_headers(iter, end);\r\n    }\r\n    catch (const std::exception&) {\r\n        return std::nullopt;\r\n    }\r\n\r\n    return header;\r\n}\r\n\r\nstd::optional<http_response_header> http_header_parser::parse_response_header(std::string::const_iterator begin, std::string::const_iterator end)\r\n{\r\n    auto iter = begin;\r\n    auto tmp = iter;\r\n    for (;iter != end && *iter != ' ' && *iter != '\\r'; ++iter)\r\n        ;\r\n    if (std::distance(tmp, iter) < 6 || iter == end || *iter != ' ' || !std::equal(tmp, tmp + 5, \"HTTP/\")) return std::nullopt;\r\n    http_response_header header;\r\n    header._http_version = std::string(tmp + 5, iter);\r\n    tmp = ++iter;\r\n    for (;iter != end && *iter != ' ' && *iter != '\\r'; ++iter)\r\n        ;\r\n    if (tmp == iter || iter == end) return std::nullopt;\r\n    try {\r\n        header._status_code = std::stoul(std::string(tmp, iter));\r\n    }\r\n    catch(const std::exception&) {\r\n        return std::nullopt;\r\n    }\r\n\r\n    if (*iter == ' ') {\r\n        tmp = ++iter;\r\n        for (;iter != end && *iter != '\\r'; ++iter)\r\n            ;\r\n        if (iter == end || *iter != '\\r') return std::nullopt;\r\n        header._status_description = std::string(tmp, iter);\r\n    }\r\n\r\n    if (*iter != '\\r') return std::nullopt;\r\n\r\n    if (iter == end || *(++iter) != '\\n') return std::nullopt;\r\n\r\n    ++iter;\r\n    try {\r\n        header._headers_map = parse_headers(iter, end);\r\n    }\r\n    catch (const std::exception&) {\r\n        return std::nullopt;\r\n    }\r\n\r\n    return header;\r\n}\r\n\r\n}; // namespace azure_proxy\r\n"
  },
  {
    "path": "src/http_header_parser.hpp",
    "content": "/*\r\n *    http_header_parser.hpp:\r\n *\r\n *    Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n#ifndef AZURE_HTTP_HEADER_PARSER_HPP\r\n#define AZURE_HTTP_HEADER_PARSER_HPP\r\n\r\n#include <algorithm>\r\n#include <cctype>\r\n#include <map>\r\n#include <optional>\r\n#include <string>\r\n\r\nnamespace azure_proxy {\r\n\r\nstruct default_filed_name_compare {\r\n    bool operator() (const std::string& str1, const std::string& str2) const {\r\n        return std::lexicographical_compare(str1.begin(), str1.end(), str2.begin(), str2.end(), [](const char ch1, const char ch2) -> bool {\r\n            return std::tolower(static_cast<unsigned char>(ch1)) < std::tolower(static_cast<unsigned char>(ch2));\r\n        });\r\n    }\r\n};\r\ntypedef std::multimap<const std::string, std::string, default_filed_name_compare> http_headers_container;\r\n\r\nclass http_request_header {\r\n    friend class http_header_parser;\r\n    std::string _method;\r\n    std::string _scheme;\r\n    std::string _host;\r\n    unsigned short _port;\r\n    std::string _path_and_query;\r\n    std::string _http_version;\r\n    http_headers_container _headers_map;\r\n    http_request_header();\r\npublic:\r\n    const std::string& method() const;\r\n    const std::string& scheme() const;\r\n    const std::string& host() const;\r\n    unsigned short port() const;\r\n    const std::string& path_and_query() const;\r\n    const std::string& http_version() const;\r\n    std::optional<std::string> get_header_value(const std::string& name) const;\r\n    std::size_t erase_header(const std::string& name);\r\n    const http_headers_container& get_headers_map() const;\r\n};\r\n\r\nclass http_response_header {\r\n    friend class http_header_parser;\r\n    std::string _http_version;\r\n    unsigned int _status_code;\r\n    std::string _status_description;\r\n    http_headers_container _headers_map;\r\n    http_response_header();\r\npublic:\r\n    const std::string& http_version() const;\r\n    unsigned int status_code() const;\r\n    const std::string& status_description() const;\r\n    std::optional<std::string> get_header_value(const std::string& name) const;\r\n    std::size_t erase_header(const std::string& name);\r\n    const http_headers_container& get_headers_map() const;\r\n};\r\n\r\nclass http_header_parser {\r\n    static http_headers_container parse_headers(std::string::const_iterator begin, std::string::const_iterator end);\r\npublic:\r\n    static std::optional<http_request_header> parse_request_header(std::string::const_iterator begin, std::string::const_iterator end);\r\n    static std::optional<http_response_header> parse_response_header(std::string::const_iterator begin, std::string::const_iterator end);\r\n};\r\n\r\n}; // namespace azure_proxy\r\n\r\n#endif\r\n"
  },
  {
    "path": "src/http_proxy_client.cpp",
    "content": "﻿/*\r\n *    http_proxy_client.cpp:\r\n *\r\n *    Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n#include <iostream>\r\n#include <string>\r\n#include <thread>\r\n#include <utility>\r\n#include <vector>\r\n\r\n#include \"http_proxy_client.hpp\"\r\n#include \"http_proxy_client_connection.hpp\"\r\n#include \"http_proxy_client_config.hpp\"\r\n\r\nnamespace azure_proxy {\r\n\r\nhttp_proxy_client::http_proxy_client(net::io_context& io_ctx) :\r\n    io_ctx(io_ctx),\r\n    acceptor(io_ctx)\r\n{\r\n}\r\n\r\nvoid http_proxy_client::run()\r\n{\r\n    const auto& config = http_proxy_client_config::get_instance();\r\n    net::ip::tcp::endpoint endpoint(net::ip::make_address(config.get_bind_address()), config.get_listen_port());\r\n    this->acceptor.open(endpoint.protocol());\r\n#ifndef _WIN32\r\n    this->acceptor.set_option(net::ip::tcp::acceptor::reuse_address(true));\r\n#endif\r\n    this->acceptor.bind(endpoint);\r\n    this->acceptor.listen(net::socket_base::max_listen_connections);\r\n    this->start_accept();\r\n\r\n    std::vector<std::thread> td_vec;\r\n    for (auto i = 0u; i < config.get_workers(); ++i) {\r\n        td_vec.emplace_back([this]() {\r\n            try {\r\n                this->io_ctx.run();\r\n            }\r\n            catch (const std::exception& e) {\r\n                std::cerr << e.what() << std::endl;\r\n            }\r\n        });\r\n    }\r\n\r\n    for (auto& td : td_vec) {\r\n        td.join();\r\n    }\r\n}\r\n\r\nvoid http_proxy_client::start_accept()\r\n{\r\n    this->acceptor.async_accept([this](const std::error_code& error, net::ip::tcp::socket socket) {\r\n        if (!error) {\r\n            auto connection = http_proxy_client_connection::create(this->io_ctx, std::move(socket));\r\n            connection->start();\r\n        }\r\n        this->start_accept();\r\n    });\r\n}\r\n\r\n} // namespace azure_proxy\r\n"
  },
  {
    "path": "src/http_proxy_client.hpp",
    "content": "﻿/*\r\n *    http_proxy_client.hpp:\r\n *\r\n *    Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n#ifndef AZURE_HTTP_PROXY_CLIENT_HPP\r\n#define AZURE_HTTP_PROXY_CLIENT_HPP\r\n\r\n#include <experimental/net>\r\n\r\nnamespace net = std::experimental::net;\r\n\r\nnamespace azure_proxy {\r\n\r\nclass http_proxy_client {\r\n    net::io_context& io_ctx;\r\n    net::ip::tcp::acceptor acceptor;\r\npublic:\r\n    http_proxy_client(net::io_context& io_ctx);\r\n    void run();\r\nprivate:\r\n    void start_accept();\r\n};\r\n\r\n} // namespace azure_proxy\r\n\r\n#endif\r\n"
  },
  {
    "path": "src/http_proxy_client_config.cpp",
    "content": "/*\r\n *    http_proxy_client_config.cpp:\r\n *\r\n *    Copyright (C) 2013-2024 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n#include <algorithm>\r\n#include <cctype>\r\n#include <fstream>\r\n#include <memory>\r\n#include <string>\r\n\r\n#ifdef _WIN32\r\n#include <codecvt>\r\n#include <Windows.h>\r\n#endif\r\n\r\n#include \"encrypt.hpp\"\r\n#include \"http_proxy_client_config.hpp\"\r\n#include <jsonxx.h>\r\n\r\nnamespace azure_proxy {\r\n\r\nhttp_proxy_client_config::http_proxy_client_config()\r\n{}\r\n\r\nbool http_proxy_client_config::load_config_data(const std::string& config_data)\r\n{\r\n    bool rollback = true;\r\n    std::shared_ptr<bool> auto_rollback(&rollback, [this](bool* rollback) {\r\n        if (*rollback) {\r\n            this->config_map.clear();\r\n        }\r\n    });\r\n\r\n    jsonxx::Object json_obj;\r\n    if (!json_obj.parse(config_data)) {\r\n        std::cerr << \"Failed to parse config\" << std::endl;\r\n        return false;\r\n    }\r\n    if (!json_obj.has<jsonxx::String>(\"proxy_server_address\")) {\r\n        std::cerr << \"Could not find \\\"proxy_server_address\\\" in config or it's value is not a string\" << std::endl;\r\n        return false;\r\n    }\r\n    this->config_map[\"proxy_server_address\"] = std::string(json_obj.get<jsonxx::String>(\"proxy_server_address\"));\r\n    if (!json_obj.has<jsonxx::Number>(\"proxy_server_port\")) {\r\n        std::cerr << \"Could not find \\\"proxy_server_port\\\" in config or it's value is not a number\" << std::endl;\r\n        return false;\r\n    }\r\n    this->config_map[\"proxy_server_port\"] = static_cast<unsigned short>(json_obj.get<jsonxx::Number>(\"proxy_server_port\"));\r\n    if (json_obj.has<jsonxx::String>(\"bind_address\")) {\r\n        this->config_map[\"bind_address\"] = std::string(json_obj.get<jsonxx::String>(\"bind_address\"));\r\n    }\r\n    else {\r\n        this->config_map[\"bind_address\"] = std::string(\"127.0.0.1\");\r\n    }\r\n    if (json_obj.has<jsonxx::Number>(\"listen_port\")) {\r\n        this->config_map[\"listen_port\"] = static_cast<unsigned short>(json_obj.get<jsonxx::Number>(\"listen_port\"));\r\n    }\r\n    else {\r\n        this->config_map[\"listen_port\"] = static_cast<unsigned short>(8089);\r\n    }\r\n    if (!json_obj.has<jsonxx::String>(\"rsa_public_key\")) {\r\n        std::cerr << \"Could not find \\\"rsa_public_key\\\" in config or it's value is not a string\" << std::endl;\r\n        return false;\r\n    }\r\n    const std::string& rsa_public_key = json_obj.get<jsonxx::String>(\"rsa_public_key\");\r\n    try {\r\n        rsa rsa_pub(rsa_public_key);\r\n        if (rsa_pub.modulus_size() < 128) {\r\n            std::cerr << \"Must use RSA keys of at least 1024 bits\" << std::endl;\r\n            return false;\r\n        }\r\n    }\r\n    catch (const std::exception&) {\r\n        std::cerr << \"The value of rsa_public_key is bad\" << std::endl;\r\n        return false;\r\n    }\r\n    this->config_map[\"rsa_public_key\"] = rsa_public_key;\r\n    if (json_obj.has<jsonxx::String>(\"cipher\")) {\r\n        std::string cipher = std::string(json_obj.get<jsonxx::String>(\"cipher\"));\r\n        for (auto& ch : cipher) {\r\n            ch = std::tolower(static_cast<unsigned char>(ch));\r\n        }\r\n        bool is_supported_cipher = false;\r\n        if (cipher.size() > 3 && std::equal(cipher.begin(), cipher.begin() + 4, \"aes-\")) {\r\n            if (cipher.size() > 8 && cipher[7] == '-'\r\n                && (std::equal(cipher.begin() + 4, cipher.begin() + 7, \"128\")\r\n                    || std::equal(cipher.begin() + 4, cipher.begin() + 7, \"192\")\r\n                    || std::equal(cipher.begin() + 4, cipher.begin() + 7, \"256\")\r\n                    )) {\r\n                    if (std::equal(cipher.begin() + 8, cipher.end(), \"cfb\")\r\n                        || std::equal(cipher.begin() + 8, cipher.end(), \"cfb128\")\r\n                        || std::equal(cipher.begin() + 8, cipher.end(), \"ofb\")\r\n                        || std::equal(cipher.begin() + 8, cipher.end(), \"ofb128\")\r\n                        || std::equal(cipher.begin() + 8, cipher.end(), \"ctr\")\r\n                        || std::equal(cipher.begin() + 8, cipher.end(), \"ctr128\")) {\r\n                            is_supported_cipher = true;\r\n                    }\r\n            }\r\n        }\r\n        if (!is_supported_cipher) {\r\n            std::cerr << \"Unsupported cipher: \" << cipher << std::endl;\r\n            return false;\r\n        }\r\n        this->config_map[\"cipher\"] = cipher;\r\n    }\r\n    else {\r\n        this->config_map[\"cipher\"] = std::string(\"aes-256-cfb\");\r\n    }\r\n    if (json_obj.has<jsonxx::Number>(\"timeout\")) {\r\n        int timeout = static_cast<int>(json_obj.get<jsonxx::Number>(\"timeout\"));\r\n        this->config_map[\"timeout\"] = static_cast<unsigned int>(timeout < 30 ? 30 : timeout);\r\n    }\r\n    else {\r\n        this->config_map[\"timeout\"] = 240u;\r\n    }\r\n    if (json_obj.has<jsonxx::Number>(\"workers\")) {\r\n        int threads = static_cast<int>(json_obj.get<jsonxx::Number>(\"workers\"));\r\n        this->config_map[\"workers\"] = static_cast<unsigned int>(threads < 1 ? 1 : (threads > 16 ? 16 : threads));\r\n    }\r\n    else {\r\n        this->config_map[\"workers\"] = 2u;\r\n    }\r\n    std::string auth_key;\r\n    if (json_obj.has<jsonxx::String>(\"auth_key\")) {\r\n        auth_key = std::string(json_obj.get<jsonxx::String>(\"auth_key\"));\r\n    }\r\n    this->config_map[\"auth_key\"] = auth_key;\r\n\r\n    rollback = false;\r\n    return true;\r\n}\r\n\r\nbool http_proxy_client_config::load_config(const std::string& config_path)\r\n{\r\n    std::string config_data;\r\n#ifdef _WIN32\r\n    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;\r\n    std::wstring config_file_path = converter.from_bytes(config_path);\r\n    std::shared_ptr<std::remove_pointer<HANDLE>::type> config_file_handle(\r\n        CreateFileW(config_file_path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL),\r\n        [](HANDLE native_handle) {\r\n            if (native_handle != INVALID_HANDLE_VALUE) {\r\n                CloseHandle(native_handle);\r\n            }\r\n    });\r\n    if (config_file_handle.get() == INVALID_HANDLE_VALUE) {\r\n        std::cerr << \"Failed to open config file \\\"client.json\\\"\" << std::endl;\r\n        return false;\r\n    }\r\n    char ch;\r\n    DWORD size_read = 0;\r\n    BOOL read_result = ReadFile(config_file_handle.get(), &ch, 1, &size_read, NULL);\r\n    while (read_result != FALSE && size_read != 0) {\r\n        config_data.push_back(ch);\r\n        read_result =  ReadFile(config_file_handle.get(), &ch, 1, &size_read, NULL);\r\n    }\r\n    if (read_result == FALSE) {\r\n        std::cerr << \"Failed to read data from config file\" << std::endl;\r\n        return false;\r\n    }\r\n#else\r\n    std::ifstream ifile(config_path.c_str());\r\n    if (!ifile.is_open()) {\r\n        std::cerr << \"Failed to open \\\"\" << config_path << \"\\\"\" << std::endl;\r\n        return false;\r\n    }\r\n    char ch;\r\n    while (ifile.get(ch)) {\r\n        config_data.push_back(ch);\r\n    }\r\n#endif\r\n    return this->load_config_data(config_data);\r\n}\r\n\r\nconst std::string& http_proxy_client_config::get_proxy_server_address() const\r\n{\r\n    return this->get_config_value<const std::string&>(\"proxy_server_address\");\r\n}\r\n\r\nunsigned short http_proxy_client_config::get_proxy_server_port() const\r\n{\r\n    return this->get_config_value<unsigned short>(\"proxy_server_port\");\r\n}\r\n\r\nconst std::string& http_proxy_client_config::get_bind_address() const\r\n{\r\n    return this->get_config_value<const std::string&>(\"bind_address\");\r\n}\r\n\r\nunsigned short http_proxy_client_config::get_listen_port() const\r\n{\r\n    return this->get_config_value<unsigned short>(\"listen_port\");\r\n}\r\n\r\nconst std::string& http_proxy_client_config::get_rsa_public_key() const\r\n{\r\n    return this->get_config_value<const std::string&>(\"rsa_public_key\");\r\n}\r\n\r\nconst std::string& http_proxy_client_config::get_cipher() const\r\n{\r\n    return this->get_config_value<const std::string&>(\"cipher\");\r\n}\r\n\r\nunsigned int http_proxy_client_config::get_timeout() const\r\n{\r\n    return this->get_config_value<unsigned int>(\"timeout\");\r\n}\r\n\r\nunsigned int http_proxy_client_config::get_workers() const\r\n{\r\n    return this->get_config_value<unsigned int>(\"workers\");\r\n}\r\n\r\nconst std::string& http_proxy_client_config::get_auth_key() const\r\n{\r\n    return this->get_config_value<const std::string&>(\"auth_key\");\r\n}\r\n\r\nhttp_proxy_client_config& http_proxy_client_config::get_instance()\r\n{\r\n    static http_proxy_client_config instance;\r\n    return instance;\r\n}\r\n\r\n} // namespace azure_proxy\r\n"
  },
  {
    "path": "src/http_proxy_client_config.hpp",
    "content": "/*\r\n *    http_proxy_client_config.hpp:\r\n *\r\n *    Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n#ifndef AZURE_HTTP_PROXY_CLIENT_CONFIG_HPP\r\n#define AZURE_HTTP_PROXY_CLIENT_CONFIG_HPP\r\n\r\n#include <any>\r\n#include <cassert>\r\n#include <map>\r\n#include <stdexcept>\r\n#include <string>\r\n\r\nnamespace azure_proxy {\r\n\r\nclass http_proxy_client_config {\r\n    std::map<std::string, std::any> config_map;\r\nprivate:\r\n    template<typename T>\r\n    T get_config_value(const std::string& key) const {\r\n        assert(!this->config_map.empty());\r\n        auto iter = this->config_map.find(key);\r\n        if (iter == this->config_map.end()) {\r\n            throw std::invalid_argument(\"invalid argument\");\r\n        }\r\n        return std::any_cast<T>(iter->second);\r\n    }\r\n    http_proxy_client_config();\r\n    bool load_config_data(const std::string& config_data);\r\npublic:\r\n    bool load_config(const std::string& config_path);\r\n    const std::string& get_proxy_server_address() const;\r\n    unsigned short get_proxy_server_port() const;\r\n    const std::string& get_bind_address() const;\r\n    unsigned short get_listen_port() const;\r\n    const std::string& get_rsa_public_key() const;\r\n    const std::string& get_cipher() const;\r\n    unsigned int get_timeout() const;\r\n    unsigned int get_workers() const;\r\n    const std::string& get_auth_key() const;\r\n\r\n    static http_proxy_client_config& get_instance();\r\n};\r\n\r\n} // namespace azure_proxy\r\n\r\n#endif\r\n"
  },
  {
    "path": "src/http_proxy_client_connection.cpp",
    "content": "﻿/*\r\n *    http_proxy_client_connection.cpp:\r\n *\r\n *    Copyright (C) 2013-2024 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n#include <algorithm>\r\n#include <cstring>\r\n#include <utility>\r\n\r\n#include \"http_proxy_client_config.hpp\"\r\n#include \"http_proxy_client_connection.hpp\"\r\n#include \"key_generator.hpp\"\r\n#include \"hash_utils.hpp\"\r\n\r\nnamespace azure_proxy {\r\n\r\nhttp_proxy_client_connection::http_proxy_client_connection(net::io_context& io_ctx, net::ip::tcp::socket&& ua_socket) :\r\n    strand(io_ctx.get_executor()),\r\n    user_agent_socket(std::move(ua_socket)),\r\n    proxy_server_socket(io_ctx),\r\n    resolver(io_ctx),\r\n    connection_state(proxy_connection_state::ready),\r\n    timer(io_ctx),\r\n    timeout(std::chrono::seconds(http_proxy_client_config::get_instance().get_timeout()))\r\n{\r\n}\r\n\r\nhttp_proxy_client_connection::~http_proxy_client_connection()\r\n{\r\n}\r\n\r\nstd::shared_ptr<http_proxy_client_connection> http_proxy_client_connection::create(net::io_context& io_ctx, net::ip::tcp::socket&& ua_socket)\r\n{\r\n    return std::shared_ptr<http_proxy_client_connection>(new http_proxy_client_connection(io_ctx, std::move(ua_socket)));\r\n}\r\n\r\nvoid http_proxy_client_connection::start()\r\n{\r\n    std::array<unsigned char, 86> cipher_info_raw;\r\n    cipher_info_raw.fill(0);\r\n    // 0 ~ 2\r\n    cipher_info_raw[0] = 'A';\r\n    cipher_info_raw[1] = 'H';\r\n    cipher_info_raw[2] = 'P';\r\n\r\n    // 3 protocol version\r\n    // - 0: version 1.0\r\n    // - 1: since version 1.1, support 32 bytes auth_key_hash\r\n    unsigned char protocol_version = 1;\r\n    const auto &auth_key = http_proxy_client_config::get_instance().get_auth_key();\r\n    bool has_auth_key = !auth_key.empty();\r\n    if (!has_auth_key) {\r\n        // Use old protocol to be compatible with version 1.0\r\n        protocol_version = 0;\r\n    }\r\n\r\n    // cipher code\r\n    // 0x00 aes-128-cfb\r\n    // 0x01 aes-128-cfb8\r\n    // 0x02 aes-128-cfb1\r\n    // 0x03 aes-128-ofb\r\n    // 0x04 aes-128-ctr\r\n    // 0x05 aes-192-cfb\r\n    // 0x06 aes-192-cfb8\r\n    // 0x07 aes-192-cfb1\r\n    // 0x08 aes-192-ofb\r\n    // 0x09 aes-192-ctr\r\n    // 0x0A aes-256-cfb\r\n    // 0x0B aes-256-cfb8\r\n    // 0x0C aes-256-cfb1\r\n    // 0x0D aes-256-ofb\r\n    // 0x0E aes-256-ctr\r\n\r\n    unsigned char cipher_code = 0;\r\n    std::vector<unsigned char> ivec(16);\r\n    std::vector<unsigned char> key_vec;\r\n    const auto& cipher_name = http_proxy_client_config::get_instance().get_cipher();\r\n    if (cipher_name.size() > 7 && std::equal(cipher_name.begin(), cipher_name.begin() + 3, \"aes\")) {\r\n        // aes\r\n        assert(cipher_name[3] == '-' && cipher_name[7] == '-');\r\n        if (std::strcmp(cipher_name.c_str() + 8, \"cfb\") == 0 || std::strcmp(cipher_name.c_str() + 8, \"cfb128\") == 0) {\r\n            // aes-xxx-cfb\r\n            if (std::equal(cipher_name.begin() + 4, cipher_name.begin() + 7, \"128\")) {\r\n                cipher_code = 0x00;\r\n                key_vec.resize(128 / 8);\r\n            }\r\n            else if (std::equal(cipher_name.begin() + 4, cipher_name.begin() + 7, \"192\")) {\r\n                cipher_code = 0x05;\r\n                key_vec.resize(192 / 8);\r\n            }\r\n            else {\r\n                cipher_code = 0x0A;\r\n                key_vec.resize(256 / 8);\r\n            }\r\n            key_generator::get_instance().generate(ivec.data(), ivec.size());\r\n            key_generator::get_instance().generate(key_vec.data(), key_vec.size());\r\n            this->encryptor = std::unique_ptr<stream_encryptor>(new aes_cfb128_encryptor(key_vec.data(), key_vec.size() * 8, ivec.data()));\r\n            this->decryptor = std::unique_ptr<stream_decryptor>(new aes_cfb128_decryptor(key_vec.data(), key_vec.size() * 8, ivec.data()));\r\n        }\r\n        else if (std::strcmp(cipher_name.c_str() + 8, \"cfb8\") == 0) {\r\n            // aes-xxx-cfb8(deprecated)\r\n        }\r\n        else if (std::strcmp(cipher_name.c_str() + 8, \"cfb1\") == 0) {\r\n            // aes-xxx-cfb1(deprecated)\r\n        }\r\n        else if (std::strcmp(cipher_name.c_str() + 8, \"ofb\") == 0) {\r\n            // aes-xxx-ofb\r\n            if (std::equal(cipher_name.begin() + 4, cipher_name.begin() + 7, \"128\")) {\r\n                cipher_code = 0x03;\r\n                key_vec.resize(128 / 8);\r\n            }\r\n            else if (std::equal(cipher_name.begin() + 4, cipher_name.begin() + 7, \"192\")) {\r\n                cipher_code = 0x08;\r\n                key_vec.resize(192 / 8);\r\n            }\r\n            else {\r\n                cipher_code = 0x0D;\r\n                key_vec.resize(256 / 8);\r\n            }\r\n            key_generator::get_instance().generate(ivec.data(), ivec.size());\r\n            key_generator::get_instance().generate(key_vec.data(), key_vec.size());\r\n            this->encryptor = std::unique_ptr<stream_encryptor>(new aes_ofb128_encryptor(key_vec.data(), key_vec.size() * 8, ivec.data()));\r\n            this->decryptor = std::unique_ptr<stream_decryptor>(new aes_ofb128_decryptor(key_vec.data(), key_vec.size() * 8, ivec.data()));\r\n        }\r\n        else if (std::strcmp(cipher_name.c_str() + 8, \"ctr\") == 0) {\r\n            // aes-xxx-ctr\r\n            if (std::equal(cipher_name.begin() + 4, cipher_name.begin() + 7, \"128\")) {\r\n                cipher_code = 0x04;\r\n                key_vec.resize(128 / 8);\r\n            }\r\n            else if (std::equal(cipher_name.begin() + 4, cipher_name.begin() + 7, \"192\")) {\r\n                cipher_code = 0x09;\r\n                key_vec.resize(192 / 8);\r\n            }\r\n            else {\r\n                cipher_code = 0x0E;\r\n                key_vec.resize(256 / 8);\r\n            }\r\n            std::fill(ivec.begin(), ivec.end(), 0);\r\n            key_generator::get_instance().generate(key_vec.data(), key_vec.size());\r\n            this->encryptor = std::unique_ptr<stream_encryptor>(new aes_ctr128_encryptor(key_vec.data(), key_vec.size() * 8, ivec.data()));\r\n            this->decryptor = std::unique_ptr<stream_decryptor>(new aes_ctr128_decryptor(key_vec.data(), key_vec.size() * 8, ivec.data()));\r\n        }\r\n    }\r\n\r\n    if (!this->encryptor || !this->decryptor) {\r\n        return;\r\n    }\r\n\r\n    // 3 protocol version\r\n    cipher_info_raw[3] = protocol_version;\r\n    if (protocol_version == 0) {\r\n        // 4 zero\r\n        // 5 cipher code\r\n        cipher_info_raw[5] = cipher_code;\r\n        // 6 zero\r\n        // 7 ~ 22 ivec\r\n        std::copy(ivec.cbegin(), ivec.cend(), cipher_info_raw.begin() + 7);\r\n        // 23 ~ (38/46/54) cipher key\r\n        std::copy(key_vec.cbegin(), key_vec.cend(), cipher_info_raw.begin() + 23);\r\n    } else {\r\n        // 4 auth type\r\n        // - 0 no authentication\r\n        // - 1 32 bytes auth_key_hash\r\n        unsigned char auth_type = has_auth_key ? 1 : 0;\r\n        cipher_info_raw[4] = auth_type;\r\n        // 5 cipher code\r\n        cipher_info_raw[5] = cipher_code;\r\n        // 6 ~ 37 auth_key_hash\r\n        if (auth_type == 1) {\r\n            auto auth_key_hash = hash_utils::sha256(reinterpret_cast<const unsigned char*>(auth_key.data()), auth_key.size());\r\n            std::copy(auth_key_hash.cbegin(), auth_key_hash.cend(), cipher_info_raw.begin() + 6);\r\n        }\r\n        // 38 ~ 53 ivec\r\n        std::copy(ivec.cbegin(), ivec.cend(), cipher_info_raw.begin() + 38);\r\n        // 54 ~ (69/77/85) cipher key\r\n        std::copy(key_vec.cbegin(), key_vec.cend(), cipher_info_raw.begin() + 54);\r\n    }\r\n\r\n    rsa rsa_pub(http_proxy_client_config::get_instance().get_rsa_public_key());\r\n    if (rsa_pub.modulus_size() < 128) {\r\n        return;\r\n    }\r\n\r\n    this->encrypted_cipher_info.resize(rsa_pub.modulus_size());\r\n    if(this->encrypted_cipher_info.size() != rsa_pub.encrypt(cipher_info_raw.size(), cipher_info_raw.data(), this->encrypted_cipher_info.data())) {\r\n        return;\r\n    }\r\n\r\n    auto self(this->shared_from_this());\r\n    const auto& proxy_server_address = http_proxy_client_config::get_instance().get_proxy_server_address();\r\n    auto proxy_server_port = std::to_string(http_proxy_client_config::get_instance().get_proxy_server_port());\r\n    this->connection_state = proxy_connection_state::resolve_proxy_server_address;\r\n    this->set_timer();\r\n    this->resolver.async_resolve(proxy_server_address, proxy_server_port, net::bind_executor(this->strand, [this, self](const std::error_code& error, net::ip::tcp::resolver::results_type results) {\r\n        if (this->cancel_timer()) {\r\n            if (!error) {\r\n                this->connection_state = proxy_connection_state::connecte_to_proxy_server;\r\n                this->set_timer();\r\n                this->proxy_server_socket.async_connect(*results.cbegin(), net::bind_executor(this->strand, [this, self](const std::error_code& error) {\r\n                    if (this->cancel_timer()) {\r\n                        if (!error) {\r\n                            this->on_connection_established();\r\n                        }\r\n                        else {\r\n                            this->on_error(error);\r\n                        }\r\n                    }\r\n                }));\r\n            }\r\n            else {\r\n                this->on_error(error);\r\n            }\r\n        }\r\n    }));\r\n}\r\n\r\nvoid http_proxy_client_connection::async_read_data_from_user_agent()\r\n{\r\n    auto self(this->shared_from_this());\r\n    this->set_timer();\r\n    this->user_agent_socket.async_read_some(net::buffer(this->upgoing_buffer_read.data(), this->upgoing_buffer_read.size()), net::bind_executor(this->strand, [this, self](const std::error_code& error, std::size_t bytes_transferred) {\r\n        if (this->cancel_timer()) {\r\n            if (!error) {\r\n                this->encryptor->encrypt(reinterpret_cast<const unsigned char*>(&this->upgoing_buffer_read[0]), reinterpret_cast<unsigned char*>(&this->upgoing_buffer_write[0]), bytes_transferred);\r\n                this->async_write_data_to_proxy_server(this->upgoing_buffer_write.data(), 0, bytes_transferred);\r\n            }\r\n            else {\r\n                this->on_error(error);\r\n            }\r\n        }\r\n    }));\r\n}\r\n\r\nvoid http_proxy_client_connection::async_read_data_from_proxy_server(bool set_timer)\r\n{\r\n    auto self(this->shared_from_this());\r\n    if (set_timer) {\r\n        this->set_timer();\r\n    }\r\n    this->proxy_server_socket.async_read_some(net::buffer(this->downgoing_buffer_read.data(), this->downgoing_buffer_read.size()), net::bind_executor(this->strand, [this, self](const std::error_code& error, std::size_t bytes_transferred) {\r\n        if (this->cancel_timer()) {\r\n            if (!error) {\r\n                this->decryptor->decrypt(reinterpret_cast<const unsigned char*>(&this->downgoing_buffer_read[0]), reinterpret_cast<unsigned char*>(&this->downgoing_buffer_write[0]), bytes_transferred);\r\n                this->async_write_data_to_user_agent(this->downgoing_buffer_write.data(), 0, bytes_transferred);\r\n            }\r\n            else {\r\n                this->on_error(error);\r\n            }\r\n        }\r\n    }));\r\n}\r\n\r\nvoid http_proxy_client_connection::async_write_data_to_user_agent(const char* write_buffer, std::size_t offset, std::size_t size)\r\n{\r\n    auto self(this->shared_from_this());\r\n    this->set_timer();\r\n    this->user_agent_socket.async_write_some(net::buffer(write_buffer + offset, size),\r\n        net::bind_executor(this->strand, [this, self, write_buffer, offset, size](const std::error_code& error, std::size_t bytes_transferred) {\r\n        if (this->cancel_timer()) {\r\n            if (!error) {\r\n                if (bytes_transferred < size) {\r\n                    this->async_write_data_to_user_agent(write_buffer, offset + bytes_transferred, size - bytes_transferred);\r\n                }\r\n                else {\r\n                    this->async_read_data_from_proxy_server();\r\n                }\r\n            }\r\n            else {\r\n                this->on_error(error);\r\n            }\r\n        }\r\n    }));\r\n}\r\n\r\nvoid http_proxy_client_connection::async_write_data_to_proxy_server(const char* write_buffer, std::size_t offset, std::size_t size)\r\n{\r\n    auto self(this->shared_from_this());\r\n    this->set_timer();\r\n    this->proxy_server_socket.async_write_some(net::buffer(write_buffer + offset, size),\r\n        net::bind_executor(this->strand, [this, self, write_buffer, offset, size](const std::error_code& error, std::size_t bytes_transferred) {\r\n            if (this->cancel_timer()) {\r\n                if (!error) {\r\n                    if (bytes_transferred < size) {\r\n                        this->async_write_data_to_proxy_server(write_buffer, offset + bytes_transferred, size - bytes_transferred);\r\n                    }\r\n                    else {\r\n                        this->async_read_data_from_user_agent();\r\n                    }\r\n                }\r\n                else {\r\n                    this->on_error(error);\r\n                }\r\n            }\r\n        })\r\n    );\r\n}\r\n\r\nvoid http_proxy_client_connection::set_timer()\r\n{\r\n    if (this->timer.expires_after(this->timeout) != 0) {\r\n        assert(false);\r\n    }\r\n    auto self(this->shared_from_this());\r\n    this->timer.async_wait(net::bind_executor(this->strand, [this, self](const std::error_code& error) {\r\n        if (error != net::error::operation_aborted) {\r\n            this->on_timeout();\r\n        }\r\n    }));\r\n}\r\n\r\nbool http_proxy_client_connection::cancel_timer()\r\n{\r\n    std::size_t ret = this->timer.cancel();\r\n    assert(ret <= 1);\r\n    return ret == 1;\r\n}\r\n\r\nvoid http_proxy_client_connection::on_connection_established()\r\n{\r\n    this->async_write_data_to_proxy_server(reinterpret_cast<const char*>(this->encrypted_cipher_info.data()), 0, this->encrypted_cipher_info.size());\r\n    this->async_read_data_from_proxy_server(false);\r\n}\r\n\r\nvoid http_proxy_client_connection::on_error(const std::error_code& error)\r\n{\r\n    this->cancel_timer();\r\n    std::error_code ec;\r\n    if (this->proxy_server_socket.is_open()) {\r\n        this->proxy_server_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);\r\n        this->proxy_server_socket.close(ec);\r\n    }\r\n    if (this->user_agent_socket.is_open()) {\r\n        this->user_agent_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);\r\n        this->user_agent_socket.close(ec);\r\n    }\r\n}\r\n\r\nvoid http_proxy_client_connection::on_timeout()\r\n{\r\n    if (this->connection_state == proxy_connection_state::resolve_proxy_server_address) {\r\n        this->resolver.cancel();\r\n    }\r\n    else {\r\n        std::error_code ec;\r\n        if (this->proxy_server_socket.is_open()) {\r\n            this->proxy_server_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);\r\n            this->proxy_server_socket.close(ec);\r\n        }\r\n        if (this->user_agent_socket.is_open()) {\r\n            this->user_agent_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);\r\n            this->user_agent_socket.close(ec);\r\n        }\r\n    }\r\n}\r\n\r\n} // namespace azure_proxy\r\n"
  },
  {
    "path": "src/http_proxy_client_connection.hpp",
    "content": "﻿/*\r\n *    http_proxy_client_connection.hpp:\r\n *\r\n *    Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n#ifndef AZURE_HTTP_PROXY_CLIENT_CONNECTION_HPP\r\n#define AZURE_HTTP_PROXY_CLIENT_CONNECTION_HPP\r\n\r\n#include <array>\r\n#include <chrono>\r\n#include <experimental/net>\r\n#include <memory>\r\n#include <vector>\r\n\r\n#include \"encrypt.hpp\"\r\n\r\nconst std::size_t BUFFER_LENGTH = 2048;\r\n\r\nnamespace net = std::experimental::net;\r\n\r\nnamespace azure_proxy {\r\n\r\nclass http_proxy_client_connection : public std::enable_shared_from_this<http_proxy_client_connection> {\r\n    enum class proxy_connection_state {\r\n        ready,\r\n        resolve_proxy_server_address,\r\n        connecte_to_proxy_server,\r\n        tunnel_transfer\r\n    };\r\nprivate:\r\n    net::strand<net::io_context::executor_type> strand;\r\n    net::ip::tcp::socket user_agent_socket;\r\n    net::ip::tcp::socket proxy_server_socket;\r\n    net::ip::tcp::resolver resolver;\r\n    proxy_connection_state connection_state;\r\n    net::basic_waitable_timer<std::chrono::steady_clock> timer;\r\n    std::vector<unsigned char> encrypted_cipher_info;\r\n    std::array<char, BUFFER_LENGTH> upgoing_buffer_read;\r\n    std::array<char, BUFFER_LENGTH> upgoing_buffer_write;\r\n    std::array<char, BUFFER_LENGTH> downgoing_buffer_read;\r\n    std::array<char, BUFFER_LENGTH> downgoing_buffer_write;\r\n    std::unique_ptr<stream_encryptor> encryptor;\r\n    std::unique_ptr<stream_decryptor> decryptor;\r\n    std::chrono::seconds timeout;\r\nprivate:\r\n    http_proxy_client_connection(net::io_context& io_ctx, net::ip::tcp::socket&& ua_socket);\r\npublic:\r\n    ~http_proxy_client_connection();\r\n    static std::shared_ptr<http_proxy_client_connection> create(net::io_context& io_ctx, net::ip::tcp::socket&& ua_socket);\r\n    void start();\r\nprivate:\r\n    void async_read_data_from_user_agent();\r\n    void async_read_data_from_proxy_server(bool set_timer = true);\r\n    void async_write_data_to_user_agent(const char* write_buffer, std::size_t offset, std::size_t size);\r\n    void async_write_data_to_proxy_server(const char* write_buffer, std::size_t offset, std::size_t size);\r\n\r\n    void set_timer();\r\n    bool cancel_timer();\r\n\r\n    void on_connection_established();\r\n    void on_error(const std::error_code& error);\r\n    void on_timeout();\r\n};\r\n\r\n} // namespace azure_proxy\r\n\r\n#endif\r\n"
  },
  {
    "path": "src/http_proxy_client_main.cpp",
    "content": "﻿/*\r\n *    http_proxy_client_main.cpp:\r\n *\r\n *    Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n#include <experimental/net>\r\n#include <csignal>\r\n#include <cstdlib>\r\n#include <iostream>\r\n#include <string>\r\n#include <vector>\r\n\r\n#include \"http_proxy_client.hpp\"\r\n#include \"http_proxy_client_config.hpp\"\r\n#include \"version.hpp\"\r\n\r\n#ifdef _WIN32\r\n#include <codecvt>\r\n#include <shellapi.h>\r\n#endif\r\n\r\nnamespace net = std::experimental::net;\r\n\r\nstruct ClientArgs {\r\n    std::string config_file = \"client.json\";\r\n};\r\n\r\nvoid print_usage() {\r\n#ifdef _WIN32\r\n    const char *prog = \"ahpc.exe\";\r\n#else\r\n    const char *prog = \"ahpc\";\r\n#endif\r\n    std::cout << \"Usage: \" << prog << \" [options]\\n\\n\"\r\n              << \"options:\\n\"\r\n              << \"  -h, --help              Show this help message and exit\\n\"\r\n              << \"  -v, --version           Print the program version and exit\\n\"\r\n              << \"  -c, --config PATH       Configuration file path (default: client.json)\\n\";\r\n}\r\n\r\nstatic ClientArgs parse_args(const std::vector<std::string>& argv) {\r\n    std::string arg;\r\n    bool invalid_param = false;\r\n    ClientArgs args;\r\n\r\n    for (std::size_t i = 1; i < argv.size(); ++i) {\r\n        arg = argv[i];\r\n\r\n        if (arg == \"-h\" || arg == \"--help\") {\r\n            print_usage();\r\n            std::exit(EXIT_SUCCESS);\r\n        } else if (arg == \"-v\" || arg == \"--version\") {\r\n            std::cout << \"Version: \" << AHP_VERSION_STRING << std::endl;\r\n            std::exit(EXIT_SUCCESS);\r\n        } else if (arg == \"-c\" || arg == \"--config\") {\r\n            if (++i >= argv.size()) {\r\n                invalid_param = true;\r\n                break;\r\n            }\r\n            args.config_file = argv[i];\r\n        } else {\r\n            std::cerr << \"Unknown argument: \" << arg << std::endl;\r\n            print_usage();\r\n            std::exit(EXIT_FAILURE);\r\n        }\r\n    }\r\n\r\n    if (invalid_param) {\r\n        std::cerr << \"Invalid parameter for argument: \" << arg << std::endl;\r\n        std::exit(EXIT_FAILURE);\r\n    }\r\n\r\n    return args;\r\n}\r\n\r\nstatic ClientArgs parse_args(int argc, char** argv) {\r\n    std::vector<std::string> argv_vec;\r\n    argv_vec.reserve(argc);\r\n\r\n#ifdef _WIN32\r\n    LPWSTR *wargs = CommandLineToArgvW(GetCommandLineW(), &argc);\r\n    if (wargs == nullptr) {\r\n        std::cerr << \"Failed to retrieve command line arguments\" << std::endl;\r\n        std::exit(EXIT_FAILURE);\r\n    }\r\n\r\n    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;\r\n    for (std::size_t i = 0; i < argc; ++i) {\r\n        argv_vec.emplace_back(converter.to_bytes(wargs[i]));\r\n    }\r\n\r\n    LocalFree(wargs);\r\n#else\r\n    for (std::size_t i = 0; i < argc; ++i) {\r\n        argv_vec.emplace_back(argv[i]);\r\n    }\r\n#endif\r\n\r\n    return parse_args(argv_vec);\r\n}\r\n\r\nstd::weak_ptr<net::io_context> wp_io_ctx;\r\nstatic void signal_handler(int signal)\r\n{\r\n    auto io_ctx = wp_io_ctx.lock();\r\n    if (io_ctx) {\r\n        io_ctx->stop();\r\n    }\r\n}\r\n\r\nint main(int argc, char** argv)\r\n{\r\n    using namespace azure_proxy;\r\n    auto args = parse_args(argc, argv);\r\n    try {\r\n        auto& config = http_proxy_client_config::get_instance();\r\n        if (config.load_config(args.config_file)) {\r\n            std::cout << \"AHP client version \" << AHP_VERSION_STRING << std::endl;\r\n            std::cout << \"server address: \" << config.get_proxy_server_address() << ':' << config.get_proxy_server_port() << std::endl;\r\n            std::cout << \"local address: \" << config.get_bind_address() << ':' << config.get_listen_port() << std::endl;\r\n            std::cout << \"cipher: \" << config.get_cipher() << std::endl;\r\n            auto io_ctx = std::make_shared<net::io_context>();\r\n            wp_io_ctx = io_ctx;\r\n            http_proxy_client client(*io_ctx);\r\n            std::signal(SIGINT, signal_handler);\r\n            std::signal(SIGTERM, signal_handler);\r\n            client.run();\r\n        }\r\n    }\r\n    catch (std::exception& e) {\r\n        std::cerr << \"Exception: \" << e.what() << \"\\n\";\r\n    }\r\n}\r\n"
  },
  {
    "path": "src/http_proxy_server.cpp",
    "content": "﻿/*\r\n *    http_proxy_server.cpp:\r\n *\r\n *    Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n#include <iostream>\r\n#include <memory>\r\n#include <thread>\r\n#include <vector>\r\n\r\n#include \"http_proxy_server.hpp\"\r\n#include \"http_proxy_server_config.hpp\"\r\n#include \"http_proxy_server_connection.hpp\"\r\n\r\nnamespace azure_proxy {\r\n\r\nhttp_proxy_server::http_proxy_server(net::io_context& io_ctx) :\r\n    io_ctx(io_ctx),\r\n    acceptor(io_ctx)\r\n{\r\n}\r\n\r\nvoid http_proxy_server::run()\r\n{\r\n    const auto& config = http_proxy_server_config::get_instance();\r\n    net::ip::tcp::endpoint endpoint(net::ip::make_address(config.get_bind_address()), config.get_listen_port());\r\n    this->acceptor.open(endpoint.protocol());\r\n#ifndef _WIN32\r\n    this->acceptor.set_option(net::ip::tcp::acceptor::reuse_address(true));\r\n#endif\r\n    this->acceptor.bind(endpoint);\r\n    this->acceptor.listen(net::socket_base::max_listen_connections);\r\n    this->start_accept();\r\n\r\n    std::vector<std::thread> td_vec;\r\n\r\n    for (auto i = 0u; i < config.get_workers(); ++i) {\r\n        td_vec.emplace_back([this]() {\r\n            try {\r\n                this->io_ctx.run();\r\n            }\r\n            catch (const std::exception& e) {\r\n                std::cerr << e.what() << std::endl;\r\n            }\r\n        });\r\n    }\r\n\r\n    for (auto& td : td_vec) {\r\n        td.join();\r\n    }\r\n}\r\n\r\nvoid http_proxy_server::start_accept()\r\n{\r\n    this->acceptor.async_accept([this](const std::error_code& error, net::ip::tcp::socket socket) {\r\n        if (!error) {\r\n            auto connection = http_proxy_server_connection::create(this->io_ctx, std::move(socket));\r\n            connection->start();\r\n        }\r\n        this->start_accept();\r\n    });\r\n}\r\n\r\n} //namespace azure_proxy\r\n"
  },
  {
    "path": "src/http_proxy_server.hpp",
    "content": "﻿/*\r\n *    http_proxy_server.hpp:\r\n *\r\n *    Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n#ifndef AZURE_HTTP_PROXY_SERVER_HPP\r\n#define AZURE_HTTP_PROXY_SERVER_HPP\r\n\r\n#include <experimental/net>\r\n\r\nnamespace net = std::experimental::net;\r\n\r\nnamespace azure_proxy {\r\n\r\n    class http_proxy_server {\r\n        net::io_context& io_ctx;\r\n        net::ip::tcp::acceptor acceptor;\r\n    public:\r\n        http_proxy_server(net::io_context& io_ctx);\r\n        void run();\r\n    private:\r\n        void start_accept();\r\n    };\r\n\r\n} // namespace azure_proxy\r\n\r\n#endif\r\n"
  },
  {
    "path": "src/http_proxy_server_config.cpp",
    "content": "/*\r\n *    http_proxy_server_config.cpp:\r\n *\r\n *    Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n#include <algorithm>\r\n#include <cctype>\r\n#include <fstream>\r\n#include <memory>\r\n#include <string>\r\n\r\n#ifdef _WIN32\r\n#include <codecvt>\r\n#include <Windows.h>\r\n#endif\r\n\r\n#include \"authentication.hpp\"\r\n#include \"encrypt.hpp\"\r\n#include \"http_proxy_server_config.hpp\"\r\n#include <jsonxx.h>\r\n\r\nnamespace azure_proxy {\r\n\r\nhttp_proxy_server_config::http_proxy_server_config()\r\n{\r\n}\r\n\r\nbool http_proxy_server_config::load_config_data(const std::string& config_data)\r\n{\r\n    bool rollback = true;\r\n    std::shared_ptr<bool> auto_rollback(&rollback, [this](bool* rollback) {\r\n        if (*rollback) {\r\n            this->config_map.clear();\r\n            authentication::get_instance().remove_all_auth_keys();\r\n        }\r\n    });\r\n\r\n    jsonxx::Object json_obj;\r\n    if (!json_obj.parse(config_data)) {\r\n        std::cerr << \"Failed to parse config\" << std::endl;\r\n        return false;\r\n    }\r\n    if (json_obj.has<jsonxx::String>(\"bind_address\")) {\r\n        this->config_map[\"bind_address\"] = std::string(json_obj.get<jsonxx::String>(\"bind_address\"));\r\n    }\r\n    else {\r\n        this->config_map[\"bind_address\"] = std::string(\"0.0.0.0\");\r\n    }\r\n    if (json_obj.has<jsonxx::Number>(\"listen_port\")) {\r\n        this->config_map[\"listen_port\"] = static_cast<unsigned short>(json_obj.get<jsonxx::Number>(\"listen_port\"));\r\n    }\r\n    else {\r\n        this->config_map[\"listen_port\"] = static_cast<unsigned short>(8090);\r\n    }\r\n    if (!json_obj.has<jsonxx::String>(\"rsa_private_key\")) {\r\n        std::cerr << \"Could not find \\\"rsa_private_key\\\" in config or it's value is not a string\" << std::endl;\r\n        return false;\r\n    }\r\n    const std::string& rsa_private_key = json_obj.get<jsonxx::String>(\"rsa_private_key\");\r\n    try {\r\n        rsa rsa_pri(rsa_private_key);\r\n        if (rsa_pri.modulus_size() < 128) {\r\n            std::cerr << \"Must use RSA keys of at least 1024 bits\" << std::endl;\r\n            return false;\r\n        }\r\n    }\r\n    catch (const std::exception&) {\r\n        std::cerr << \"The value of rsa_private_key is bad\" << std::endl;\r\n        return false;\r\n    }\r\n    this->config_map[\"rsa_private_key\"] = rsa_private_key;\r\n    if (json_obj.has<jsonxx::Number>(\"timeout\")) {\r\n        int timeout = static_cast<int>(json_obj.get<jsonxx::Number>(\"timeout\"));\r\n        this->config_map[\"timeout\"] = static_cast<unsigned int>(timeout < 30 ? 30 : timeout);\r\n    }\r\n    else {\r\n        this->config_map[\"timeout\"] = 240u;\r\n    }\r\n    if (json_obj.has<jsonxx::Number>(\"workers\")) {\r\n        int threads = static_cast<int>(json_obj.get<jsonxx::Number>(\"workers\"));\r\n        this->config_map[\"workers\"] = static_cast<unsigned int>(threads < 1 ? 1 : (threads > 16 ? 16 : threads));\r\n    }\r\n    else {\r\n        this->config_map[\"workers\"] = 4u;\r\n    }\r\n    if (json_obj.has<jsonxx::Boolean>(\"auth\") && json_obj.get<jsonxx::Boolean>(\"auth\")) {\r\n        this->config_map[\"auth\"] = true;\r\n        if (!json_obj.has<jsonxx::Array>(\"auth_key_list\")) {\r\n            std::cerr << \"Could not find \\\"auth_key_list\\\" in config or it's value is not a array\" << std::endl;\r\n            return false;\r\n        }\r\n        const jsonxx::Array& auth_key_list = json_obj.get<jsonxx::Array>(\"auth_key_list\");\r\n        for (size_t i = 0; i < auth_key_list.size(); ++i) {\r\n            if (!auth_key_list.has<jsonxx::String>(i)) {\r\n                std::cerr << \"The value of \\\"auth_key_list\\\" contains unexpected element\" << std::endl;\r\n                return false;\r\n            }\r\n            const auto &auth_key = auth_key_list.get<jsonxx::String>(i);\r\n            if (auth_key.empty()) {\r\n                std::cerr << \"Ignore empty \\\"auth_key\\\" at index: \" << i << std::endl;\r\n                continue;\r\n            }\r\n            authentication::get_instance().add_auth_key(auth_key);\r\n        }\r\n    }\r\n    else {\r\n        this->config_map[\"auth\"] = false;\r\n    }\r\n\r\n    rollback = false;\r\n    return true;\r\n}\r\n\r\nbool http_proxy_server_config::load_config(const std::string& config_path)\r\n{\r\n    std::string config_data;\r\n#ifdef _WIN32\r\n    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;\r\n    std::wstring config_file_path = converter.from_bytes(config_path);\r\n    std::shared_ptr<std::remove_pointer<HANDLE>::type> config_file_handle(\r\n        CreateFileW(config_file_path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL),\r\n        [](HANDLE native_handle) {\r\n            if (native_handle != INVALID_HANDLE_VALUE) {\r\n                CloseHandle(native_handle);\r\n            }\r\n    });\r\n    if (config_file_handle.get() == INVALID_HANDLE_VALUE) {\r\n        std::cerr << \"Failed to open config file \\\"server.json\\\"\" << std::endl;\r\n        return false;\r\n    }\r\n    char ch;\r\n    DWORD size_read = 0;\r\n    BOOL read_result =  ReadFile(config_file_handle.get(), &ch, 1, &size_read, NULL);\r\n    while (read_result != FALSE && size_read != 0) {\r\n        config_data.push_back(ch);\r\n        read_result =  ReadFile(config_file_handle.get(), &ch, 1, &size_read, NULL);\r\n    }\r\n    if (read_result == FALSE) {\r\n        std::cerr << \"Failed to read data from config file\" << std::endl;\r\n        return false;\r\n    }\r\n#else\r\n    std::ifstream ifile(config_path.c_str());\r\n    if (!ifile.is_open()) {\r\n        std::cerr << \"Failed to open \\\"\" << config_path << \"\\\"\" << std::endl;\r\n        return false;\r\n    }\r\n    char ch;\r\n    while (ifile.get(ch)) {\r\n        config_data.push_back(ch);\r\n    }\r\n#endif\r\n    return this->load_config_data(config_data);\r\n}\r\n\r\nconst std::string& http_proxy_server_config::get_bind_address() const\r\n{\r\n    return this->get_config_value<const std::string&>(\"bind_address\");\r\n}\r\n\r\nunsigned short http_proxy_server_config::get_listen_port() const\r\n{\r\n    return this->get_config_value<unsigned short>(\"listen_port\");\r\n}\r\n\r\nconst std::string& http_proxy_server_config::get_rsa_private_key() const\r\n{\r\n    return this->get_config_value<const std::string&>(\"rsa_private_key\");\r\n}\r\n\r\nunsigned int http_proxy_server_config::get_timeout() const\r\n{\r\n    return this->get_config_value<unsigned int>(\"timeout\");\r\n}\r\n\r\nunsigned int http_proxy_server_config::get_workers() const\r\n{\r\n    return this->get_config_value<unsigned int>(\"workers\");\r\n}\r\n\r\nbool http_proxy_server_config::enable_auth() const\r\n{\r\n    return this->get_config_value<bool>(\"auth\");\r\n}\r\n\r\nhttp_proxy_server_config& http_proxy_server_config::get_instance()\r\n{\r\n    static http_proxy_server_config instance;\r\n    return instance;\r\n}\r\n\r\n} // namespace azure_proxy\r\n"
  },
  {
    "path": "src/http_proxy_server_config.hpp",
    "content": "/*\r\n *    http_proxy_server_config.hpp:\r\n *\r\n *    Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n#ifndef AZURE_HTTP_PROXY_SERVER_CONFIG_HPP\r\n#define AZURE_HTTP_PROXY_SERVER_CONFIG_HPP\r\n\r\n#include <any>\r\n#include <cassert>\r\n#include <map>\r\n#include <stdexcept>\r\n#include <string>\r\n\r\nnamespace azure_proxy {\r\n\r\nclass http_proxy_server_config {\r\n    std::map<std::string, std::any> config_map;\r\nprivate:\r\n    template<typename T>\r\n    T get_config_value(const std::string& key) const {\r\n        assert(!this->config_map.empty());\r\n        auto iter = this->config_map.find(key);\r\n        if (iter == this->config_map.end()) {\r\n            throw std::invalid_argument(\"invalid argument\");\r\n        }\r\n        return std::any_cast<T>(iter->second);\r\n    }\r\n    http_proxy_server_config();\r\n    bool load_config_data(const std::string& config_data);\r\npublic:\r\n    bool load_config(const std::string& config_path);\r\n    const std::string& get_bind_address() const;\r\n    unsigned short get_listen_port() const;\r\n    const std::string& get_rsa_private_key() const;\r\n    unsigned int get_timeout() const;\r\n    unsigned int get_workers() const;\r\n    bool enable_auth() const;\r\n\r\n    static http_proxy_server_config& get_instance();\r\n};\r\n\r\n} // namespace azure_proxy\r\n\r\n#endif\r\n"
  },
  {
    "path": "src/http_proxy_server_connection.cpp",
    "content": "/*\r\n *    http_proxy_server_connection.cpp:\r\n *\r\n *    Copyright (C) 2013-2024 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n#include <cctype>\r\n#include <algorithm>\r\n#include <cassert>\r\n#include <cstring>\r\n\r\n#include \"authentication.hpp\"\r\n#include \"http_proxy_server_config.hpp\"\r\n#include \"http_proxy_server_connection.hpp\"\r\n\r\nstatic const std::size_t MAX_REQUEST_HEADER_LENGTH = 10240;\r\nstatic const std::size_t MAX_RESPONSE_HEADER_LENGTH = 10240;\r\n\r\nnamespace azure_proxy {\r\n\r\nhttp_proxy_server_connection::http_proxy_server_connection(net::io_context& io_ctx, net::ip::tcp::socket&& proxy_client_socket) :\r\n    strand(io_ctx.get_executor()),\r\n    proxy_client_socket(std::move(proxy_client_socket)),\r\n    origin_server_socket(io_ctx),\r\n    resolver(io_ctx),\r\n    timer(io_ctx),\r\n    rsa_pri(http_proxy_server_config::get_instance().get_rsa_private_key())\r\n{\r\n    this->connection_context.connection_state = proxy_connection_state::read_cipher_data;\r\n}\r\n\r\nhttp_proxy_server_connection::~http_proxy_server_connection()\r\n{\r\n}\r\n\r\nstd::shared_ptr<http_proxy_server_connection> http_proxy_server_connection::create(net::io_context& io_ctx, net::ip::tcp::socket&& client_socket)\r\n{\r\n    return std::shared_ptr<http_proxy_server_connection>(new http_proxy_server_connection(io_ctx, std::move(client_socket)));\r\n}\r\n\r\nvoid http_proxy_server_connection::start()\r\n{\r\n    this->connection_context.connection_state = proxy_connection_state::read_cipher_data;\r\n    this->async_read_data_from_proxy_client(1, std::min<std::size_t>(this->rsa_pri.modulus_size(), BUFFER_LENGTH));\r\n}\r\n\r\nvoid http_proxy_server_connection::async_read_data_from_proxy_client(std::size_t at_least_size, std::size_t at_most_size)\r\n{\r\n    assert(at_least_size <= at_most_size && at_most_size <= BUFFER_LENGTH);\r\n    auto self(this->shared_from_this());\r\n    this->set_timer();\r\n    net::async_read(this->proxy_client_socket,\r\n        net::buffer(&this->upgoing_buffer_read[0], at_most_size),\r\n        net::transfer_at_least(at_least_size),\r\n        net::bind_executor(this->strand, [this, self](const std::error_code& error, std::size_t bytes_transferred) {\r\n            if (this->cancel_timer()) {\r\n                if (!error) {\r\n                    this->on_proxy_client_data_arrived(bytes_transferred);\r\n                }\r\n                else {\r\n                    this->on_error(error);\r\n                }\r\n            }\r\n        })\r\n    );\r\n}\r\n\r\nvoid http_proxy_server_connection::async_read_data_from_origin_server(bool set_timer, std::size_t at_least_size, std::size_t at_most_size)\r\n{\r\n    auto self(this->shared_from_this());\r\n    if (set_timer) {\r\n        this->set_timer();\r\n    }\r\n    net::async_read(this->origin_server_socket,\r\n        net::buffer(&this->downgoing_buffer_read[0], at_most_size),\r\n        net::transfer_at_least(at_least_size),\r\n        net::bind_executor(this->strand, [this, self](const std::error_code& error, std::size_t bytes_transferred) {\r\n            if (this->cancel_timer()) {\r\n                if (!error) {\r\n                    this->on_origin_server_data_arrived(bytes_transferred);\r\n                }\r\n                else {\r\n                    this->on_error(error);\r\n                }\r\n            }\r\n        })\r\n    );\r\n}\r\n\r\nvoid http_proxy_server_connection::async_connect_to_origin_server()\r\n{\r\n    this->connection_context.reconnect_on_error = false;\r\n    if (this->origin_server_socket.is_open()) {\r\n        if (this->request_header->method() == \"CONNECT\") {\r\n            std::error_code ec;\r\n            this->origin_server_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);\r\n            this->origin_server_socket.close(ec);\r\n        }\r\n    }\r\n\r\n    if (this->origin_server_socket.is_open() &&\r\n        this->request_header->host() == this->connection_context.origin_server_name &&\r\n        this->request_header->port() == this->connection_context.origin_server_port) {\r\n        this->connection_context.reconnect_on_error = true;\r\n        this->on_connect();\r\n    }\r\n    else {\r\n        this->connection_context.origin_server_name = this->request_header->host();\r\n        this->connection_context.origin_server_port = this->request_header->port();\r\n        auto self(this->shared_from_this());\r\n        this->connection_context.connection_state = proxy_connection_state::resolve_origin_server_address;\r\n        this->set_timer();\r\n        auto host = this->request_header->host();\r\n        if (host.size() >= 2 && host[0] == '[' && host[host.size() - 1] == ']') {\r\n            host = host.substr(1, host.size() - 2);\r\n        }\r\n        this->resolver.async_resolve(host, std::to_string(this->connection_context.origin_server_port),\r\n            net::bind_executor(this->strand, [this, self](const std::error_code& error, net::ip::tcp::resolver::results_type results) {\r\n                if (this->cancel_timer()) {\r\n                    if (!error) {\r\n                        this->resolve_results = results;\r\n                        this->on_resolved(results.cbegin());\r\n                    }\r\n                    else {\r\n                        this->on_error(error);\r\n                    }\r\n                }\r\n            })\r\n        );\r\n    }\r\n}\r\n\r\nvoid http_proxy_server_connection::async_write_request_header_to_origin_server()\r\n{\r\n    auto request_content_begin = this->request_data.begin() + this->request_data.find(\"\\r\\n\\r\\n\") + 4;\r\n    this->modified_request_data = this->request_header->method();\r\n    this->modified_request_data.push_back(' ');\r\n    this->modified_request_data += this->request_header->path_and_query();\r\n    this->modified_request_data += \" HTTP/\";\r\n    this->modified_request_data += this->request_header->http_version();\r\n    this->modified_request_data += \"\\r\\n\"; \r\n\r\n    this->request_header->erase_header(\"Proxy-Connection\");\r\n    this->request_header->erase_header(\"Proxy-Authorization\");\r\n\r\n    for (const auto& header: this->request_header->get_headers_map()) {\r\n        this->modified_request_data += std::get<0>(header);\r\n        this->modified_request_data += \": \";\r\n        this->modified_request_data += std::get<1>(header);\r\n        this->modified_request_data += \"\\r\\n\";\r\n    }\r\n    this->modified_request_data += \"\\r\\n\";\r\n    this->modified_request_data.append(request_content_begin, this->request_data.end());\r\n    this->connection_context.connection_state = proxy_connection_state::write_http_request_header;\r\n    this->async_write_data_to_origin_server(this->modified_request_data.data(), 0, this->modified_request_data.size());\r\n}\r\n\r\nvoid http_proxy_server_connection::async_write_response_header_to_proxy_client()\r\n{\r\n    auto response_content_begin = this->response_data.begin() + this->response_data.find(\"\\r\\n\\r\\n\") + 4;\r\n\r\n    this->modified_response_data = \"HTTP/\";\r\n    this->modified_response_data += this->response_header->http_version();\r\n    this->modified_response_data.push_back(' ');\r\n    this->modified_response_data += std::to_string(this->response_header->status_code());\r\n    if (!this->response_header->status_description().empty()) {\r\n        this->modified_response_data.push_back(' ');\r\n        this->modified_response_data += this->response_header->status_description();\r\n    }\r\n    this->modified_response_data += \"\\r\\n\";\r\n\r\n    for (const auto& header: this->response_header->get_headers_map()) {\r\n        this->modified_response_data += std::get<0>(header);\r\n        this->modified_response_data += \": \";\r\n        this->modified_response_data += std::get<1>(header);\r\n        this->modified_response_data += \"\\r\\n\";\r\n    }\r\n    this->modified_response_data += \"\\r\\n\";\r\n    this->modified_response_data.append(response_content_begin, this->response_data.end());\r\n    unsigned char temp_buffer[256];\r\n    std::size_t blocks = this->modified_response_data.size() / 256;\r\n    if (this->modified_response_data.size() % 256 != 0) {\r\n        blocks += 1;\r\n    }\r\n    for (std::size_t i = 0; i < blocks; ++i) {\r\n        std::size_t block_length = 256;\r\n        if ((i + 1) * 256 > this->modified_response_data.size()) {\r\n            block_length = this->modified_response_data.size() % 256;\r\n        }\r\n        std::copy(reinterpret_cast<const unsigned char*>(&this->modified_response_data[i * 256]), reinterpret_cast<const unsigned char*>(&this->modified_response_data[i * 256 + block_length]), temp_buffer);\r\n        this->encryptor->encrypt(temp_buffer, reinterpret_cast<unsigned char*>(&this->modified_response_data[i * 256]), block_length);\r\n    }\r\n    this->connection_context.connection_state = proxy_connection_state::write_http_response_header;\r\n    this->async_write_data_to_proxy_client(this->modified_response_data.data(), 0, this->modified_response_data.size());\r\n}\r\n\r\nvoid http_proxy_server_connection::async_write_data_to_origin_server(const char* write_buffer, std::size_t offset, std::size_t size)\r\n{\r\n    auto self(this->shared_from_this());\r\n    this->set_timer();\r\n    this->origin_server_socket.async_write_some(net::buffer(write_buffer + offset, size),\r\n        net::bind_executor(this->strand, [this, self, write_buffer, offset, size](const std::error_code& error, std::size_t bytes_transferred) {\r\n            if (this->cancel_timer()) {\r\n                if (!error) { \r\n                    this->connection_context.reconnect_on_error = false;\r\n                    if (bytes_transferred < size) {\r\n                        this->async_write_data_to_origin_server(write_buffer, offset + bytes_transferred, size - bytes_transferred);\r\n                    }\r\n                    else {\r\n                        this->on_origin_server_data_written();\r\n                    }\r\n                }\r\n                else {\r\n                    this->on_error(error);\r\n                }\r\n            }\r\n        })\r\n    );\r\n}\r\n\r\nvoid http_proxy_server_connection::async_write_data_to_proxy_client(const char* write_buffer, std::size_t offset, std::size_t size)\r\n{\r\n    auto self(this->shared_from_this());\r\n    this->set_timer();\r\n    this->proxy_client_socket.async_write_some(net::buffer(write_buffer + offset, size),\r\n        net::bind_executor(this->strand, [this, self, write_buffer, offset, size](const std::error_code& error, std::size_t bytes_transferred) {\r\n            if (this->cancel_timer()) {\r\n                if (!error) {\r\n                    if (bytes_transferred < size) {\r\n                        this->async_write_data_to_proxy_client(write_buffer, offset + bytes_transferred, size - bytes_transferred);\r\n                    }\r\n                    else {\r\n                        this->on_proxy_client_data_written();\r\n                    }\r\n                }\r\n                else {\r\n                    this->on_error(error);\r\n                }\r\n            }\r\n        })\r\n    );\r\n}\r\n\r\nvoid http_proxy_server_connection::start_tunnel_transfer()\r\n{\r\n    this->connection_context.connection_state = proxy_connection_state::tunnel_transfer;\r\n    this->async_read_data_from_proxy_client();\r\n    this->async_read_data_from_origin_server(false);\r\n}\r\n\r\nvoid http_proxy_server_connection::report_error(const std::string& status_code, const std::string& status_description, const std::string& error_message)\r\n{\r\n    this->modified_response_data.clear();\r\n    this->modified_response_data += \"HTTP/1.1 \";\r\n    this->modified_response_data += status_code;\r\n    if (!status_description.empty()) {\r\n        this->modified_response_data.push_back(' ');\r\n        this->modified_response_data += status_description;\r\n    }\r\n    this->modified_response_data += \"\\r\\n\";\r\n    this->modified_response_data += \"Content-Type: text/html\\r\\n\";\r\n    this->modified_response_data += \"Server: AzureHttpProxy\\r\\n\";\r\n    this->modified_response_data += \"Content-Length: \";\r\n\r\n    std::string response_content;\r\n    response_content = \"<!DOCTYPE html><html><head><title>\";\r\n    response_content += status_code;\r\n    response_content += ' ';\r\n    response_content += status_description;\r\n    response_content += \"</title></head><body bgcolor=\\\"white\\\"><center><h1>\";\r\n    response_content += status_code;\r\n    response_content += ' ';\r\n    response_content += status_description;\r\n    response_content += \"</h1>\";\r\n    if (!error_message.empty()) {\r\n        response_content += \"<br/>\";\r\n        response_content += error_message;\r\n        response_content += \"</center>\";\r\n    }\r\n    response_content += \"<hr><center>\";\r\n    response_content += \"azure http proxy server\";\r\n    response_content += \"</center></body></html>\";\r\n    this->modified_response_data += std::to_string(response_content.size());\r\n    this->modified_response_data += \"\\r\\n\";\r\n    this->modified_response_data += \"Proxy-Connection: close\\r\\n\";\r\n    this->modified_response_data += \"\\r\\n\";\r\n    if (!this->request_header || this->request_header->method() != \"HEAD\") {\r\n        this->modified_response_data += response_content;\r\n    }\r\n    \r\n    unsigned char temp_buffer[16];\r\n    for (std::size_t i = 0; i * 16 < this->modified_response_data.size(); ++i) {\r\n        std::size_t block_length = 16;\r\n        if (this->modified_response_data.size() - i * 16 < 16) {\r\n            block_length = this->modified_response_data.size() % 16;\r\n        }\r\n        this->encryptor->encrypt(reinterpret_cast<const unsigned char*>(&this->modified_response_data[i * 16]), temp_buffer, block_length);\r\n        std::copy(temp_buffer, temp_buffer + block_length, reinterpret_cast<unsigned char*>(&this->modified_response_data[i * 16]));\r\n    }\r\n    this->connection_context.connection_state = proxy_connection_state::report_error;\r\n    auto self(this->shared_from_this());\r\n    this->async_write_data_to_proxy_client(this->modified_response_data.data(), 0 ,this->modified_response_data.size());\r\n}\r\n\r\nvoid http_proxy_server_connection::set_timer()\r\n{\r\n    if (this->timer.expires_after(std::chrono::seconds(http_proxy_server_config::get_instance().get_timeout())) != 0) {\r\n        assert(false);\r\n    }\r\n    auto self(this->shared_from_this());\r\n    this->timer.async_wait(net::bind_executor(this->strand, [this, self](const std::error_code& error) {\r\n        if (error != net::error::operation_aborted) {\r\n            this->on_timeout();\r\n        }\r\n    }));\r\n}\r\n\r\nbool http_proxy_server_connection::cancel_timer()\r\n{\r\n    std::size_t ret = this->timer.cancel();\r\n    assert(ret <= 1);\r\n    return ret == 1;\r\n}\r\n\r\nvoid http_proxy_server_connection::on_resolved(net::ip::tcp::resolver::results_type::const_iterator endpoint_iterator)\r\n{\r\n    if (this->origin_server_socket.is_open()) {\r\n        for (auto iter = endpoint_iterator; iter != this->resolve_results.cend(); ++iter) {\r\n            if (this->connection_context.origin_server_endpoint == iter->endpoint()) {\r\n                this->connection_context.reconnect_on_error = true;\r\n                this->on_connect();\r\n                return;\r\n            }\r\n            std::error_code ec;\r\n            this->origin_server_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);\r\n            this->origin_server_socket.close(ec);\r\n        }\r\n    }\r\n    this->connection_context.origin_server_endpoint = endpoint_iterator->endpoint();\r\n    auto self(this->shared_from_this());\r\n    this->connection_context.connection_state = proxy_connection_state::connect_to_origin_server;\r\n    this->set_timer();\r\n    this->origin_server_socket.async_connect(endpoint_iterator->endpoint(),\r\n        net::bind_executor(this->strand, [this, self, endpoint_iterator](const std::error_code& error) mutable {\r\n            if (this->cancel_timer()) {\r\n                if (!error) {\r\n                    this->on_connect();\r\n                }\r\n                else {\r\n                    std::error_code ec;\r\n                    this->origin_server_socket.close(ec);\r\n                    if (++endpoint_iterator != this->resolve_results.cend()) {\r\n                        this->on_resolved(endpoint_iterator);\r\n                    }\r\n                    else {\r\n                        this->on_error(error);\r\n                    }\r\n                }\r\n            }\r\n        })\r\n    );\r\n}\r\n\r\nvoid http_proxy_server_connection::on_connect()\r\n{\r\n    if (this->request_header->method() == \"CONNECT\") {\r\n        const unsigned char response_message[] = \"HTTP/1.1 200 Connection Established\\r\\nConnection: Close\\r\\n\\r\\n\";\r\n        this->modified_response_data.resize(sizeof(response_message) - 1);\r\n        this->encryptor->encrypt(response_message, const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(&this->modified_response_data[0])), this->modified_response_data.size());\r\n        this->connection_context.connection_state = proxy_connection_state::report_connection_established;\r\n        this->async_write_data_to_proxy_client(&this->modified_response_data[0], 0, this->modified_response_data.size());\r\n    }\r\n    else {\r\n        this->async_write_request_header_to_origin_server();\r\n    }\r\n}\r\n\r\nvoid http_proxy_server_connection::on_proxy_client_data_arrived(std::size_t bytes_transferred)\r\n{\r\n    if (this->connection_context.connection_state == proxy_connection_state::read_cipher_data) {\r\n        std::copy(this->upgoing_buffer_read.begin(), this->upgoing_buffer_read.begin() + bytes_transferred, std::back_inserter(this->encrypted_cipher_info));\r\n        if (this->encrypted_cipher_info.size() < this->rsa_pri.modulus_size()) {\r\n            this->async_read_data_from_proxy_client(1, std::min(static_cast<std::size_t>(this->rsa_pri.modulus_size()) - this->encrypted_cipher_info.size(), BUFFER_LENGTH));\r\n            return;\r\n        }\r\n        assert(this->encrypted_cipher_info.size() == this->rsa_pri.modulus_size());\r\n        std::vector<unsigned char> decrypted_cipher_info(this->rsa_pri.modulus_size());\r\n\r\n        if (86 != this->rsa_pri.decrypt(this->rsa_pri.modulus_size(), this->encrypted_cipher_info.data(), decrypted_cipher_info.data())) {\r\n            return;\r\n        }\r\n\r\n        if (decrypted_cipher_info[0] != 'A' ||\r\n            decrypted_cipher_info[1] != 'H' ||\r\n            decrypted_cipher_info[2] != 'P') {\r\n            return;\r\n        }\r\n\r\n        unsigned char protocol_version = decrypted_cipher_info[3];\r\n        auto need_auth = http_proxy_server_config::get_instance().enable_auth();\r\n        if (protocol_version == 0) {\r\n            if (need_auth || decrypted_cipher_info[4] != 0 || decrypted_cipher_info[6] != 0) {\r\n                return;\r\n            }\r\n        } else if (protocol_version == 1) {\r\n            if (need_auth) {\r\n                // 4 auth type\r\n                // - 0 no authentication\r\n                // - 1 32 bytes auth_key\r\n                auto auth_type = decrypted_cipher_info[4];\r\n                if (auth_type != 1) {\r\n                    return;\r\n                }\r\n                // 6 ~ 37 auth_key\r\n                std::array<unsigned char, 32> auth_key_hash;\r\n                std::copy(decrypted_cipher_info.cbegin() + 6, decrypted_cipher_info.cbegin() + 38, auth_key_hash.begin());\r\n                if (!authentication::get_instance().auth(auth_key_hash)) {\r\n                    return;\r\n                }\r\n            }\r\n        } else {\r\n            return;\r\n        }\r\n\r\n        // 5 cipher code\r\n\r\n        // 0x00 aes-128-cfb\r\n        // 0x01 aes-128-cfb8\r\n        // 0x02 aes-128-cfb1\r\n        // 0x03 aes-128-ofb\r\n        // 0x04 aes-128-ctr\r\n        // 0x05 aes-192-cfb\r\n        // 0x06 aes-192-cfb8\r\n        // 0x07 aes-192-cfb1\r\n        // 0x08 aes-192-ofb\r\n        // 0x09 aes-192-ctr\r\n        // 0x0A aes-256-cfb\r\n        // 0x0B aes-256-cfb8\r\n        // 0x0C aes-256-cfb1\r\n        // 0x0D aes-256-ofb\r\n        // 0x0E aes-256-ctr\r\n        unsigned char cipher_code = decrypted_cipher_info[5];\r\n        std::size_t ivec_offset = protocol_version == 0 ? 7 : 38;\r\n        std::size_t cipher_key_offset = protocol_version == 0 ? 23 : 54;\r\n        if (cipher_code == '\\x00' || cipher_code == '\\x05' || cipher_code == '\\x0A') {\r\n            // aes-xxx-cfb\r\n            std::size_t ivec_size = 16;\r\n            std::size_t key_bits = 256; // aes-256-cfb\r\n            if (cipher_code == '\\x00') {\r\n                // aes-128-cfb\r\n                key_bits = 128;\r\n            }\r\n            else if (cipher_code == '\\x05') {\r\n                // aes-192-cfb\r\n                key_bits = 192;\r\n            }\r\n            this->encryptor = std::unique_ptr<stream_encryptor>(new aes_cfb128_encryptor(&decrypted_cipher_info[cipher_key_offset], key_bits, &decrypted_cipher_info[ivec_offset]));\r\n            this->decryptor = std::unique_ptr<stream_decryptor>(new aes_cfb128_decryptor(&decrypted_cipher_info[cipher_key_offset], key_bits, &decrypted_cipher_info[ivec_offset]));\r\n        }\r\n        else if (cipher_code == '\\x01' || cipher_code == '\\x06' || cipher_code == '\\x0B') {\r\n            // ase-xxx-cfb8(deprecated)\r\n        }\r\n        else if (cipher_code == '\\x02' || cipher_code == '\\x07' || cipher_code == '\\x0C') {\r\n            // ase-xxx-cfb1(deprecated)\r\n        }\r\n        else if (cipher_code == '\\x03' || cipher_code == '\\x08' || cipher_code == '\\x0D') {\r\n            // ase-xxx-ofb\r\n            std::size_t ivec_size = 16;\r\n            std::size_t key_bits = 256; // aes-256-ofb\r\n            if (cipher_code == '\\x03') {\r\n                // aes-128-ofb\r\n                key_bits = 128;\r\n            }\r\n            else if (cipher_code == '\\x08') {\r\n                // aes-192-ofb\r\n                key_bits = 192;\r\n            }\r\n            this->encryptor = std::unique_ptr<stream_encryptor>(new aes_ofb128_encryptor(&decrypted_cipher_info[cipher_key_offset], key_bits, &decrypted_cipher_info[ivec_offset]));\r\n            this->decryptor = std::unique_ptr<stream_decryptor>(new aes_ofb128_decryptor(&decrypted_cipher_info[cipher_key_offset], key_bits, &decrypted_cipher_info[ivec_offset]));\r\n        }\r\n        else if (cipher_code == '\\x04' || cipher_code == '\\x09' || cipher_code == '\\x0E') {\r\n            // ase-xxx-ctr\r\n            std::size_t ivec_size = 16;\r\n            std::size_t key_bits = 256; // aes-256-ctr\r\n            if (cipher_code == '\\x04') {\r\n                // aes-128-ctr\r\n                key_bits = 128;\r\n            }\r\n            else if (cipher_code == '\\x09') {\r\n                // aes-192-ctr\r\n                key_bits = 192;\r\n            }\r\n            std::vector<unsigned char> ivec(ivec_size, 0);\r\n            this->encryptor = std::unique_ptr<stream_encryptor>(new aes_ctr128_encryptor(&decrypted_cipher_info[cipher_key_offset], key_bits, ivec.data()));\r\n            this->decryptor = std::unique_ptr<stream_decryptor>(new aes_ctr128_decryptor(&decrypted_cipher_info[cipher_key_offset], key_bits, ivec.data()));\r\n        }\r\n        if (this->encryptor == nullptr || this->decryptor == nullptr) {\r\n            return;\r\n        }\r\n        this->connection_context.connection_state = proxy_connection_state::read_http_request_header;\r\n        this->async_read_data_from_proxy_client();\r\n        return;\r\n    }\r\n    assert(this->encryptor != nullptr && this->decryptor != nullptr);\r\n    this->decryptor->decrypt(reinterpret_cast<const unsigned char*>(&this->upgoing_buffer_read[0]), reinterpret_cast<unsigned char*>(&this->upgoing_buffer_write[0]), bytes_transferred);\r\n    if (this->connection_context.connection_state == proxy_connection_state::read_http_request_header) {\r\n        const auto& decripted_data_buffer = this->upgoing_buffer_write;\r\n        this->request_data.append(decripted_data_buffer.begin(), decripted_data_buffer.begin() + bytes_transferred);\r\n        auto double_crlf_pos = this->request_data.find(\"\\r\\n\\r\\n\");\r\n        if (double_crlf_pos == std::string::npos) {\r\n            if (this->request_data.size() < MAX_REQUEST_HEADER_LENGTH) {\r\n                this->async_read_data_from_proxy_client();\r\n            }\r\n            else {\r\n                this->report_error(\"400\", \"Bad Request\", \"Request header too long\");\r\n            }\r\n            return;\r\n        }\r\n\r\n        this->request_header = http_header_parser::parse_request_header(this->request_data.begin(), this->request_data.begin() + double_crlf_pos + 4);\r\n        if (!this->request_header) {\r\n            this->report_error(\"400\", \"Bad Request\", \"Failed to parse the http request header\");\r\n            return;\r\n        }\r\n\r\n        if (this->request_header->method() != \"GET\"\r\n            // && this->request_header->method() != \"OPTIONS\"\r\n            && this->request_header->method() != \"HEAD\"\r\n            && this->request_header->method() != \"POST\"\r\n            && this->request_header->method() != \"PUT\"\r\n            && this->request_header->method() != \"DELETE\"\r\n            // && this->request_header->method() != \"TRACE\"\r\n            && this->request_header->method() != \"CONNECT\") {\r\n            this->report_error(\"405\", \"Method Not Allowed\", std::string());\r\n            return;\r\n        }\r\n        if (this->request_header->http_version() != \"1.1\"\r\n            && this->request_header->http_version() != \"1.0\") {\r\n            this->report_error(\"505\", \"HTTP Version Not Supported\", std::string());\r\n            return;\r\n        }\r\n\r\n        if (this->request_header->method() == \"CONNECT\") {\r\n            this->async_connect_to_origin_server();\r\n            return;\r\n        }\r\n        else {\r\n            if (this->request_header->scheme() != \"http\") {\r\n                this->report_error(\"400\", \"Bad Request\", \"Unsupported scheme\");\r\n                return;\r\n            }\r\n            auto proxy_connection_value = this->request_header->get_header_value(\"Proxy-Connection\");\r\n            auto connection_value = this->request_header->get_header_value(\"Connection\");\r\n            auto string_to_lower_case = [](std::string& str) {\r\n                for (auto iter = str.begin(); iter != str.end(); ++iter) {\r\n                    *iter = std::tolower(static_cast<unsigned char>(*iter));\r\n                }\r\n            };\r\n            if (proxy_connection_value) {\r\n                string_to_lower_case(*proxy_connection_value);\r\n                if (this->request_header->http_version() == \"1.1\") {\r\n                    this->read_request_context.is_proxy_client_keep_alive = true;\r\n                    if (*proxy_connection_value == \"close\") {\r\n                        this->read_request_context.is_proxy_client_keep_alive = false;\r\n                    }\r\n                }\r\n                else {\r\n                    assert(this->request_header->http_version() == \"1.0\");\r\n                    this->read_request_context.is_proxy_client_keep_alive = false;\r\n                    if (*proxy_connection_value == \"keep-alive\") {\r\n                        this->read_request_context.is_proxy_client_keep_alive = true;\r\n                    }\r\n                }\r\n            }\r\n            else {\r\n                if (this->request_header->http_version() == \"1.1\") {\r\n                    this->read_request_context.is_proxy_client_keep_alive = true;\r\n                }\r\n                else {\r\n                    this->read_request_context.is_proxy_client_keep_alive = false;\r\n                }\r\n                if (connection_value) {\r\n                    string_to_lower_case(*connection_value);\r\n                    if (this->request_header->http_version() == \"1.1\" && *connection_value == \"close\") {\r\n                        this->read_request_context.is_proxy_client_keep_alive = false;\r\n                    }\r\n                    else if (this->request_header->http_version() == \"1.0\" && *connection_value == \"keep-alive\") {\r\n                        this->read_request_context.is_proxy_client_keep_alive = true;\r\n                    }\r\n                }\r\n            }\r\n\r\n            this->read_request_context.content_length = std::optional<std::uint64_t>();\r\n            this->read_request_context.content_length_has_read = 0;\r\n            this->read_request_context.chunk_checker = std::optional<http_chunk_checker>();\r\n\r\n            if (this->request_header->method() == \"GET\" || this->request_header->method() == \"HEAD\" || this->request_header->method() == \"DELETE\") {\r\n                this->read_request_context.content_length = std::optional<std::uint64_t>(0);\r\n            }\r\n            else if (this->request_header->method() == \"POST\" || this->request_header->method() == \"PUT\") {\r\n                auto content_length_value = this->request_header->get_header_value(\"Content-Length\");\r\n                auto transfer_encoding_value = this->request_header->get_header_value(\"Transfer-Encoding\");\r\n                if (content_length_value) {\r\n                    try {\r\n                        this->read_request_context.content_length = std::optional<std::uint64_t>(std::stoull(*content_length_value));\r\n                    }\r\n                    catch (const std::exception&) {\r\n                        this->report_error(\"400\", \"Bad Request\", \"Invalid Content-Length in request\");\r\n                        return;\r\n                    }\r\n                    this->read_request_context.content_length_has_read = this->request_data.size() - (double_crlf_pos + 4);\r\n                }\r\n                else if (transfer_encoding_value) {\r\n                    string_to_lower_case(*transfer_encoding_value);\r\n                    if (*transfer_encoding_value == \"chunked\") {\r\n                        if (!this->read_request_context.chunk_checker->check(this->request_data.begin() + double_crlf_pos + 4, this->request_data.end())) {\r\n                            this->report_error(\"400\", \"Bad Request\", \"Failed to check chunked response\");\r\n                            return;\r\n                        }\r\n                        return;\r\n                    }\r\n                    else {\r\n                        this->report_error(\"400\", \"Bad Request\", \"Unsupported Transfer-Encoding\");\r\n                        return;\r\n                    }\r\n                }\r\n                else {\r\n                    this->report_error(\"411\", \"Length Required\", std::string());\r\n                    return;\r\n                }\r\n            }\r\n            else {\r\n                assert(false);\r\n                return;\r\n            }\r\n        }\r\n        this->async_connect_to_origin_server();\r\n    }\r\n    else if (this->connection_context.connection_state == proxy_connection_state::read_http_request_content) {\r\n        if (this->read_request_context.content_length) {\r\n            this->read_request_context.content_length_has_read += bytes_transferred;\r\n        }\r\n        else {\r\n            assert(this->read_request_context.chunk_checker);\r\n            if (!this->read_request_context.chunk_checker->check(this->upgoing_buffer_write.begin(), this->upgoing_buffer_write.begin() + bytes_transferred)) {\r\n                return;\r\n            }\r\n        }\r\n        this->connection_context.connection_state = proxy_connection_state::write_http_request_content;\r\n        this->async_write_data_to_origin_server(this->upgoing_buffer_write.data(), 0, bytes_transferred);\r\n    }\r\n    else if (this->connection_context.connection_state == proxy_connection_state::tunnel_transfer) {\r\n        this->async_write_data_to_origin_server(this->upgoing_buffer_write.data(), 0, bytes_transferred);\r\n    }\r\n}\r\n\r\nvoid http_proxy_server_connection::on_origin_server_data_arrived(std::size_t bytes_transferred)\r\n{\r\n    if (this->connection_context.connection_state == proxy_connection_state::read_http_response_header) {\r\n        this->response_data.append(this->downgoing_buffer_read.begin(), this->downgoing_buffer_read.begin() + bytes_transferred);\r\n        auto double_crlf_pos = this->response_data.find(\"\\r\\n\\r\\n\");\r\n        if (double_crlf_pos == std::string::npos) {\r\n            if (this->response_data.size() < MAX_RESPONSE_HEADER_LENGTH) {\r\n                this->async_read_data_from_origin_server();\r\n            }\r\n            else {\r\n                this->report_error(\"502\", \"Bad Gateway\", \"Response header too long\");\r\n            }\r\n            return;\r\n        }\r\n\r\n        this->response_header = http_header_parser::parse_response_header(this->response_data.begin(), this->response_data.begin() + double_crlf_pos + 4);\r\n        if (!this->response_header) {\r\n            this->report_error(\"502\", \"Bad Gateway\", \"Failed to parse response header\");\r\n            return;\r\n        }\r\n        if (this->response_header->http_version() != \"1.1\" && this->response_header->http_version() != \"1.0\") {\r\n            this->report_error(\"502\", \"Bad Gateway\", \"HTTP version not supported\");\r\n            return;\r\n        }\r\n        if (this->response_header->status_code() < 100 || this->response_header->status_code() > 700) {\r\n            this->report_error(\"502\", \"Bad Gateway\", \"Unexpected status code\");\r\n            return;\r\n        }\r\n        this->read_response_context.content_length = std::optional<std::uint64_t>();\r\n        this->read_response_context.content_length_has_read = 0;\r\n        this->read_response_context.is_origin_server_keep_alive = false;\r\n        this->read_response_context.chunk_checker = std::optional<http_chunk_checker>();\r\n\r\n        auto connection_value = this->response_header->get_header_value(\"Connection\");\r\n        \r\n        if (this->response_header->http_version() == \"1.1\") {\r\n            this->read_response_context.is_origin_server_keep_alive = true;\r\n        }\r\n        else {\r\n            this->read_response_context.is_origin_server_keep_alive = false;\r\n        }\r\n        auto string_to_lower_case = [](std::string& str) {\r\n            for (auto iter = str.begin(); iter != str.end(); ++iter) {\r\n                *iter = std::tolower(static_cast<unsigned char>(*iter));\r\n            }\r\n        };\r\n        if (connection_value) {\r\n            string_to_lower_case(*connection_value);\r\n            if (*connection_value == \"close\") {\r\n                this->read_response_context.is_origin_server_keep_alive = false;\r\n            }\r\n            else if (*connection_value == \"keep-alive\") {\r\n                this->read_response_context.is_origin_server_keep_alive = true;\r\n            }\r\n            else {\r\n                this->report_error(\"502\", \"Bad Gateway\", std::string());\r\n                return;\r\n            }\r\n        }\r\n\r\n        if (this->request_header->method() == \"HEAD\") {\r\n            this->read_response_context.content_length = std::optional<std::uint64_t>(0);\r\n        }\r\n        else if (this->response_header->status_code() == 204 || this->response_header->status_code() == 304) {\r\n            // 204 No Content\r\n            // 304 Not Modified\r\n            this->read_response_context.content_length = std::optional<std::uint64_t>(0);\r\n        }\r\n        else {\r\n            auto content_length_value = this->response_header->get_header_value(\"Content-Length\");\r\n            auto transfer_encoding_value = this->response_header->get_header_value(\"Transfer-Encoding\");\r\n            if (content_length_value) {\r\n                try {\r\n                    this->read_response_context.content_length = std::optional<std::uint64_t>(std::stoull(*content_length_value));\r\n                }\r\n                catch(const std::exception&) {\r\n                    this->report_error(\"502\", \"Bad Gateway\", \"Invalid Content-Length in response\");\r\n                    return;\r\n                }\r\n                this->read_response_context.content_length_has_read = this->response_data.size() - (double_crlf_pos + 4);\r\n            }\r\n            else if (transfer_encoding_value) {\r\n                string_to_lower_case(*transfer_encoding_value);\r\n                if (*transfer_encoding_value == \"chunked\") {\r\n                    this->read_response_context.chunk_checker = std::optional<http_chunk_checker>(http_chunk_checker());\r\n                    if (!this->read_response_context.chunk_checker->check(this->response_data.begin() + double_crlf_pos + 4, this->response_data.end())) {\r\n                        this->report_error(\"502\", \"Bad Gateway\", \"Failed to check chunked response\");\r\n                        return;\r\n                    }\r\n                }\r\n                else {\r\n                    this->report_error(\"502\", \"Bad Gateway\", \"Unsupported Transfer-Encoding\");\r\n                    return;\r\n                }\r\n            }\r\n            else if (this->read_response_context.is_origin_server_keep_alive) {\r\n                this->report_error(\"502\", \"Bad Gateway\", \"Miss response length info\");\r\n                return;\r\n            }\r\n        }\r\n        this->async_write_response_header_to_proxy_client();\r\n    }\r\n    else if (this->connection_context.connection_state == proxy_connection_state::read_http_response_content) {\r\n        if (this->read_response_context.content_length) {\r\n            this->read_response_context.content_length_has_read += bytes_transferred;\r\n        }\r\n        else if (this->read_response_context.chunk_checker) {\r\n            if (!this->read_response_context.chunk_checker->check(this->downgoing_buffer_read.begin(), this->downgoing_buffer_read.begin() + bytes_transferred)) {\r\n                return;\r\n            }\r\n        }\r\n        this->connection_context.connection_state = proxy_connection_state::write_http_response_content;\r\n        this->encryptor->encrypt(reinterpret_cast<const unsigned char*>(&this->downgoing_buffer_read[0]), reinterpret_cast<unsigned char*>(&this->downgoing_buffer_write[0]), bytes_transferred);\r\n        this->async_write_data_to_proxy_client(this->downgoing_buffer_write.data(), 0, bytes_transferred);\r\n    }\r\n    else if (this->connection_context.connection_state == proxy_connection_state::tunnel_transfer) {\r\n        this->encryptor->encrypt(reinterpret_cast<const unsigned char*>(&this->downgoing_buffer_read[0]), reinterpret_cast<unsigned char*>(&this->downgoing_buffer_write[0]), bytes_transferred);\r\n        this->async_write_data_to_proxy_client(this->downgoing_buffer_write.data(), 0, bytes_transferred);\r\n    }\r\n}\r\n\r\nvoid http_proxy_server_connection::on_proxy_client_data_written()\r\n{\r\n    if (this->connection_context.connection_state == proxy_connection_state::tunnel_transfer) {\r\n        this->async_read_data_from_origin_server();\r\n    }\r\n    else if (this->connection_context.connection_state == proxy_connection_state::write_http_response_header\r\n        || this->connection_context.connection_state == proxy_connection_state::write_http_response_content) {\r\n        if ((this->read_response_context.content_length && this->read_response_context.content_length_has_read >= *this->read_response_context.content_length)\r\n            || (this->read_response_context.chunk_checker && this->read_response_context.chunk_checker->is_complete())) {\r\n            std::error_code ec;\r\n            if (!this->read_response_context.is_origin_server_keep_alive) {\r\n                this->origin_server_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);\r\n                this->origin_server_socket.close(ec);\r\n            }\r\n            if (this->read_request_context.is_proxy_client_keep_alive) {\r\n                this->request_data.clear();\r\n                this->response_data.clear();\r\n                this->request_header = std::optional<http_request_header>();\r\n                this->response_header = std::optional<http_response_header>();\r\n                this->read_request_context.content_length = std::optional<std::uint64_t>();\r\n                this->read_request_context.chunk_checker = std::optional<http_chunk_checker>();\r\n                this->read_response_context.content_length = std::optional<std::uint64_t>();\r\n                this->read_response_context.chunk_checker = std::optional<http_chunk_checker>();\r\n                this->connection_context.connection_state = proxy_connection_state::read_http_request_header;\r\n                this->async_read_data_from_proxy_client();\r\n            }\r\n            else {\r\n                this->proxy_client_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);\r\n                this->proxy_client_socket.close(ec);\r\n            }\r\n        }\r\n        else {\r\n            this->connection_context.connection_state = proxy_connection_state::read_http_response_content;\r\n            this->async_read_data_from_origin_server();\r\n        }\r\n    }\r\n    else if (this->connection_context.connection_state == proxy_connection_state::report_connection_established) {\r\n        this->start_tunnel_transfer();\r\n    }\r\n    else if (this->connection_context.connection_state == proxy_connection_state::report_error) {\r\n        std::error_code ec;\r\n        if (this->origin_server_socket.is_open()) {\r\n            this->origin_server_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);\r\n            this->origin_server_socket.close(ec);\r\n        }\r\n        if (this->proxy_client_socket.is_open()) {\r\n            this->proxy_client_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);\r\n            this->proxy_client_socket.close(ec);\r\n        }\r\n    }\r\n}\r\n\r\nvoid http_proxy_server_connection::on_origin_server_data_written()\r\n{\r\n    if (this->connection_context.connection_state == proxy_connection_state::tunnel_transfer) {\r\n        this->async_read_data_from_proxy_client();\r\n    }\r\n    else if (this->connection_context.connection_state == proxy_connection_state::write_http_request_header\r\n        || this->connection_context.connection_state == proxy_connection_state::write_http_request_content) {\r\n        if (this->read_request_context.content_length) {\r\n            if (this->read_request_context.content_length_has_read < *this->read_request_context.content_length) {\r\n                this->connection_context.connection_state = proxy_connection_state::read_http_request_content;\r\n                this->async_read_data_from_proxy_client();\r\n            }\r\n            else {\r\n                this->connection_context.connection_state = proxy_connection_state::read_http_response_header;\r\n                this->async_read_data_from_origin_server();\r\n            }\r\n        }\r\n        else {\r\n            assert(this->read_request_context.chunk_checker);\r\n            if (!this->read_request_context.chunk_checker->is_complete()) {\r\n                this->connection_context.connection_state = proxy_connection_state::read_http_request_content;\r\n                this->async_read_data_from_proxy_client();\r\n            }\r\n            else {\r\n                this->connection_context.connection_state = proxy_connection_state::read_http_response_header;\r\n                this->async_read_data_from_origin_server();\r\n            }\r\n        }\r\n    }\r\n}\r\n\r\nvoid http_proxy_server_connection::on_error(const std::error_code& error)\r\n{\r\n    if (this->connection_context.connection_state == proxy_connection_state::resolve_origin_server_address) {\r\n        this->report_error(\"504\", \"Gateway Timeout\", \"Failed to resolve the hostname\");\r\n    }\r\n    else if (this->connection_context.connection_state == proxy_connection_state::connect_to_origin_server) {\r\n        this->report_error(\"502\", \"Bad Gateway\", \"Failed to connect to origin server\");\r\n    }\r\n    else if (this->connection_context.connection_state == proxy_connection_state::write_http_request_header && this->connection_context.reconnect_on_error) {\r\n        std::error_code ec;\r\n        this->origin_server_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);\r\n        this->origin_server_socket.close(ec);\r\n        this->async_connect_to_origin_server();\r\n    }\r\n    else {\r\n        std::error_code ec;\r\n        if (this->origin_server_socket.is_open()) {\r\n            this->origin_server_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);\r\n            this->origin_server_socket.close(ec);\r\n        }\r\n        if (this->proxy_client_socket.is_open()) {\r\n            this->proxy_client_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);\r\n            this->proxy_client_socket.close(ec);\r\n        }\r\n    }\r\n}\r\n\r\nvoid http_proxy_server_connection::on_timeout()\r\n{\r\n    std::error_code ec;\r\n    if (this->origin_server_socket.is_open()) {\r\n        this->origin_server_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);\r\n        this->origin_server_socket.close(ec);\r\n    }\r\n    if (this->proxy_client_socket.is_open()) {\r\n        this->proxy_client_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);\r\n        this->proxy_client_socket.close(ec);\r\n    }\r\n}\r\n\r\n} // namespace azure_proxy\r\n"
  },
  {
    "path": "src/http_proxy_server_connection.hpp",
    "content": "﻿/*\r\n *    http_proxy_server_connection.hpp:\r\n *\r\n *    Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n#ifndef AZURE_HTTP_PROXY_SERVER_CONNECTION_HPP\r\n#define AZURE_HTTP_PROXY_SERVER_CONNECTION_HPP\r\n\r\n#include <array>\r\n#include <chrono>\r\n#include <experimental/net>\r\n#include <optional>\r\n\r\n#include \"encrypt.hpp\"\r\n#include \"http_header_parser.hpp\"\r\n#include \"http_proxy_server_connection_context.hpp\"\r\n\r\nnamespace net = std::experimental::net;\r\n\r\nnamespace azure_proxy {\r\n\r\nconst std::size_t BUFFER_LENGTH = 2048;\r\n\r\nclass http_proxy_server_connection : public std::enable_shared_from_this<http_proxy_server_connection> {\r\n    net::strand<net::io_context::executor_type> strand;\r\n    net::ip::tcp::socket proxy_client_socket;\r\n    net::ip::tcp::socket origin_server_socket;\r\n    net::ip::tcp::resolver resolver;\r\n    net::ip::tcp::resolver::results_type resolve_results;\r\n    net::basic_waitable_timer<std::chrono::steady_clock> timer;\r\n    std::array<char, BUFFER_LENGTH> upgoing_buffer_read;\r\n    std::array<char, BUFFER_LENGTH> upgoing_buffer_write;\r\n    std::array<char, BUFFER_LENGTH> downgoing_buffer_read;\r\n    std::array<char, BUFFER_LENGTH> downgoing_buffer_write;\r\n    rsa rsa_pri;\r\n    std::vector<unsigned char> encrypted_cipher_info;\r\n    std::unique_ptr<stream_encryptor> encryptor;\r\n    std::unique_ptr<stream_decryptor> decryptor;\r\n    std::string request_data;\r\n    std::string modified_request_data;\r\n    std::string response_data;\r\n    std::string modified_response_data;\r\n    std::optional<http_request_header> request_header;\r\n    std::optional<http_response_header> response_header;\r\n    http_proxy_server_connection_context connection_context;\r\n    http_proxy_server_connection_read_request_context read_request_context;\r\n    http_proxy_server_connection_read_response_context read_response_context;\r\nprivate:\r\n    http_proxy_server_connection(net::io_context& io_ctx, net::ip::tcp::socket&& proxy_client_socket);\r\npublic:\r\n    ~http_proxy_server_connection();\r\n    static std::shared_ptr<http_proxy_server_connection> create(net::io_context& io_ctx, net::ip::tcp::socket&& client_socket);\r\n    void start();\r\nprivate:\r\n    void async_read_data_from_proxy_client(std::size_t at_least_size = 1, std::size_t at_most_size = BUFFER_LENGTH);\r\n    void async_read_data_from_origin_server(bool set_timer = true, std::size_t at_least_size = 1, std::size_t at_most_size = BUFFER_LENGTH);\r\n    void async_connect_to_origin_server();\r\n    void async_write_request_header_to_origin_server();\r\n    void async_write_response_header_to_proxy_client();\r\n    void async_write_data_to_origin_server(const char* write_buffer, std::size_t offset, std::size_t size);\r\n    void async_write_data_to_proxy_client(const char* write_buffer, std::size_t offset, std::size_t size);\r\n    void start_tunnel_transfer();\r\n    void report_error(const std::string& status_code, const std::string& status_description, const std::string& error_message);\r\n\r\n    void set_timer();\r\n    bool cancel_timer();\r\n\r\n    void on_resolved(net::ip::tcp::resolver::results_type::const_iterator endpoint_iterator);\r\n    void on_connect();\r\n    void on_proxy_client_data_arrived(std::size_t bytes_transferred);\r\n    void on_origin_server_data_arrived(std::size_t bytes_transferred);\r\n    void on_proxy_client_data_written();\r\n    void on_origin_server_data_written();\r\n    void on_error(const std::error_code& error);\r\n    void on_timeout();\r\n};\r\n\r\n} // namespace azure_proxy\r\n\r\n#endif\r\n"
  },
  {
    "path": "src/http_proxy_server_connection_context.hpp",
    "content": "/*\r\n *    http_proxy_server_connection_context.hpp:\r\n *\r\n *    Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n#ifndef AZURE_HTTP_PROXY_SERVER_CONNECTION_CONTEXT_HPP\r\n#define AZURE_HTTP_PROXY_SERVER_CONNECTION_CONTEXT_HPP\r\n\r\n#include <cstdint>\r\n#include <experimental/net>\r\n#include <optional>\r\n\r\n#include \"http_chunk_checker.hpp\"\r\n\r\nnamespace net = std::experimental::net;\r\n\r\nnamespace azure_proxy {\r\n\r\nenum class proxy_connection_state {\r\n    read_cipher_data,\r\n    resolve_origin_server_address,\r\n    connect_to_origin_server,\r\n    tunnel_transfer,\r\n    read_http_request_header,\r\n    write_http_request_header,\r\n    read_http_request_content,\r\n    write_http_request_content,\r\n    read_http_response_header,\r\n    write_http_response_header,\r\n    read_http_response_content,\r\n    write_http_response_content,\r\n    report_connection_established,\r\n    report_error\r\n};\r\n\r\nstruct http_proxy_server_connection_context {\r\n    proxy_connection_state connection_state;\r\n    bool reconnect_on_error;\r\n    std::string origin_server_name;\r\n    unsigned short origin_server_port;\r\n    std::optional<net::ip::tcp::endpoint> origin_server_endpoint;\r\n};\r\n\r\nstruct http_proxy_server_connection_read_request_context {\r\n    bool is_proxy_client_keep_alive;\r\n    std::optional<std::uint64_t> content_length;\r\n    std::uint64_t content_length_has_read;\r\n    std::optional<http_chunk_checker> chunk_checker;\r\n};\r\n\r\nstruct http_proxy_server_connection_read_response_context {\r\n    bool is_origin_server_keep_alive;\r\n    std::optional<std::uint64_t> content_length;\r\n    std::uint64_t content_length_has_read;\r\n    std::optional<http_chunk_checker> chunk_checker;\r\n};\r\n\r\n} // namespace azure_proxy\r\n\r\n#endif\r\n"
  },
  {
    "path": "src/http_proxy_server_main.cpp",
    "content": "﻿/*\r\n *    http_proxy_server_main.cpp:\r\n *\r\n *    Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n#include <experimental/net>\r\n#include <csignal>\r\n#include <cstdlib>\r\n#include <iostream>\r\n#include <string>\r\n#include <vector>\r\n\r\n#include \"http_proxy_server_config.hpp\"\r\n#include \"http_proxy_server.hpp\"\r\n#include \"version.hpp\"\r\n\r\n#ifdef _WIN32\r\n#include <codecvt>\r\n#include <shellapi.h>\r\n#endif\r\n\r\nnamespace net = std::experimental::net;\r\n\r\nstruct ServerArgs {\r\n    std::string config_file = \"server.json\";\r\n};\r\n\r\nvoid print_usage() {\r\n#ifdef _WIN32\r\n    const char *prog = \"ahps.exe\";\r\n#else\r\n    const char *prog = \"ahps\";\r\n#endif\r\n    std::cout << \"Usage: \" << prog << \" [options]\\n\\n\"\r\n              << \"options:\\n\"\r\n              << \"  -h, --help              Show this help message and exit\\n\"\r\n              << \"  -v, --version           Print the program version and exit\\n\"\r\n              << \"  -c, --config PATH       Configuration file path (default: server.json)\\n\";\r\n}\r\n\r\nstatic ServerArgs parse_args(const std::vector<std::string>& argv) {\r\n    std::string arg;\r\n    bool invalid_param = false;\r\n    ServerArgs args;\r\n\r\n    for (std::size_t i = 1; i < argv.size(); ++i) {\r\n        arg = argv[i];\r\n\r\n        if (arg == \"-h\" || arg == \"--help\") {\r\n            print_usage();\r\n            std::exit(EXIT_SUCCESS);\r\n        } else if (arg == \"-v\" || arg == \"--version\") {\r\n            std::cout << \"Version: \" << AHP_VERSION_STRING << std::endl;\r\n            std::exit(EXIT_SUCCESS);\r\n        } else if (arg == \"-c\" || arg == \"--config\") {\r\n            if (++i >= argv.size()) {\r\n                invalid_param = true;\r\n                break;\r\n            }\r\n            args.config_file = argv[i];\r\n        } else {\r\n            std::cerr << \"Unknown argument: \" << arg << std::endl;\r\n            print_usage();\r\n            std::exit(EXIT_FAILURE);\r\n        }\r\n    }\r\n\r\n    if (invalid_param) {\r\n        std::cerr << \"Invalid parameter for argument: \" << arg << std::endl;\r\n        std::exit(EXIT_FAILURE);\r\n    }\r\n\r\n    return args;\r\n}\r\n\r\nstatic ServerArgs parse_args(int argc, char** argv) {\r\n    std::vector<std::string> argv_vec;\r\n    argv_vec.reserve(argc);\r\n\r\n#ifdef _WIN32\r\n    LPWSTR *wargs = CommandLineToArgvW(GetCommandLineW(), &argc);\r\n    if (wargs == nullptr) {\r\n        std::cerr << \"Failed to retrieve command line arguments\" << std::endl;\r\n        std::exit(EXIT_FAILURE);\r\n    }\r\n\r\n    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;\r\n    for (std::size_t i = 0; i < argc; ++i) {\r\n        argv_vec.emplace_back(converter.to_bytes(wargs[i]));\r\n    }\r\n\r\n    LocalFree(wargs);\r\n#else\r\n    for (std::size_t i = 0; i < argc; ++i) {\r\n        argv_vec.emplace_back(argv[i]);\r\n    }\r\n#endif\r\n\r\n    return parse_args(argv_vec);\r\n}\r\n\r\nstd::weak_ptr<net::io_context> wp_io_ctx;\r\nstatic void signal_handler(int signal)\r\n{\r\n    auto io_ctx = wp_io_ctx.lock();\r\n    if (io_ctx) {\r\n        io_ctx->stop();\r\n    }\r\n}\r\n\r\nint main(int argc, char** argv)\r\n{\r\n    using namespace azure_proxy;\r\n    auto args = parse_args(argc, argv);\r\n    try {\r\n        auto& config = http_proxy_server_config::get_instance();\r\n        if (config.load_config(args.config_file)) {\r\n            std::cout << \"AHP server version \" << AHP_VERSION_STRING << std::endl;\r\n            std::cout << \"bind address: \" << config.get_bind_address() << ':' << config.get_listen_port() << std::endl;\r\n            auto io_ctx = std::make_shared<net::io_context>();\r\n            wp_io_ctx = io_ctx;\r\n            http_proxy_server server(*io_ctx);\r\n            std::signal(SIGINT, signal_handler);\r\n            std::signal(SIGTERM, signal_handler);\r\n            server.run();\r\n        }\r\n    }\r\n    catch (const std::exception& e) {\r\n        std::cerr << e.what() << std::endl;\r\n    }\r\n    return 0;\r\n}\r\n"
  },
  {
    "path": "src/key_generator.hpp",
    "content": "/*\r\n *    key_gennerator.hpp:\r\n *\r\n *    Copyright (C) 2014-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n#ifndef AZURE_KEY_GENERATOR_HPP\r\n#define AZURE_KEY_GENERATOR_HPP\r\n\r\n#include <random>\r\n#include <memory>\r\n#include <cassert>\r\n#include <chrono>\r\n#include <mutex>\r\n\r\nnamespace azure_proxy {\r\n\r\nclass key_generator {\r\n    std::mt19937 gen;\r\n    std::mutex mtx;\r\n    key_generator() {\r\n        std::uint64_t seed = reinterpret_cast<std::uint64_t>(std::unique_ptr<int>(new int(0)).get()) ^ static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count());\r\n        this->gen.seed(seed);\r\n    }\r\npublic:\r\n    void generate(unsigned char* out, std::size_t length) {\r\n        assert(out);\r\n        std::uniform_int_distribution<unsigned short> dis(0, 255);\r\n        std::lock_guard<std::mutex> lck(this->mtx);\r\n        for (std::size_t i = 0; i < length; ++i) {\r\n            out[i] = static_cast<unsigned char>(dis(this->gen));\r\n        }\r\n    }\r\n    static key_generator& get_instance() {\r\n        static key_generator instance;\r\n        return instance;\r\n    }\r\n};\r\n\r\n}\r\n\r\n#endif\r\n"
  },
  {
    "path": "src/version.hpp",
    "content": "/*\n *    version.hpp:\n *\n *    Copyright (C) 2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\n *\n */\n\n \n#ifndef AZURE_VERSION_HPP\n#define AZURE_VERSION_HPP\n\n#define AHP_MAJOR_VERSION 1\n#define AHP_MINOR_VERSION 2\n#define AHP_PATCH_VERSION 0\n\n#define AHP_VERSION_TO_STR_(x) #x\n#define AHP_VERSION_TO_STR(x) AHP_VERSION_TO_STR_(x)\n#define AHP_VERSION_DOT AHP_VERSION_TO_STR_(.)\n\n#if AHP_PATCH_VERSION > 0\n#define AHP_VERSION_STRING AHP_VERSION_TO_STR(AHP_MAJOR_VERSION) AHP_VERSION_DOT AHP_VERSION_TO_STR(AHP_MINOR_VERSION) AHP_VERSION_DOT AHP_VERSION_TO_STR(AHP_PATCH_VERSION)\n#else\n#define AHP_VERSION_STRING AHP_VERSION_TO_STR(AHP_MAJOR_VERSION) AHP_VERSION_DOT AHP_VERSION_TO_STR(AHP_MINOR_VERSION)\n#endif\n\n#endif // AZURE_VERSION_HPP\n"
  }
]