//
//  httplib.h
//
//  Copyright (c) 2020 Yuji Hirose. All rights reserved.
//  MIT License
//

#ifndef CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_HTTPLIB_H

/*
 * Configuration
 */

#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 60      //make bigger or configurable
#endif

#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0
#endif

//#ifndef CPPHTTPLIB_USE_POLL
//#define CPPHTTPLIB_USE_POLL 0
//#endif

#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5
#endif

#ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND
#define CPPHTTPLIB_READ_TIMEOUT_SECOND 20
#endif

#ifndef CPPHTTPLIB_READ_TIMEOUT_USECOND
#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0
#endif

#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
#endif

#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
#endif

#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())
#endif

#ifndef CPPHTTPLIB_RECV_BUFSIZ
#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u)
#endif

#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
#define CPPHTTPLIB_THREAD_POOL_COUNT                                           \
  ((std::max)(1u, std::thread::hardware_concurrency() - 1))
#endif

/*
 * Headers
 */

#ifdef _WIN32
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif //_CRT_SECURE_NO_WARNINGS

#ifndef _CRT_NONSTDC_NO_DEPRECATE
#define _CRT_NONSTDC_NO_DEPRECATE
#endif //_CRT_NONSTDC_NO_DEPRECATE

#if defined(_MSC_VER)
#ifdef _WIN64
using ssize_t = __int64;
#else
using ssize_t = int;
#endif

#if _MSC_VER < 1900
#define snprintf _snprintf_s
#endif
#endif // _MSC_VER

#ifndef S_ISREG
#define S_ISREG(m) (((m)&S_IFREG) == S_IFREG)
#endif // S_ISREG

#ifndef S_ISDIR
#define S_ISDIR(m) (((m)&S_IFDIR) == S_IFDIR)
#endif // S_ISDIR

#ifndef NOMINMAX
#define NOMINMAX
#endif // NOMINMAX

#include <io.h>
#include <winsock2.h>
#include <ws2tcpip.h>

#ifndef WSA_FLAG_NO_HANDLE_INHERIT
#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
#endif

#ifdef _MSC_VER
#pragma comment(lib, "ws2_32.lib")
#endif

#ifndef strcasecmp
#define strcasecmp _stricmp
#endif // strcasecmp

using socket_t = SOCKET;
#ifdef CPPHTTPLIB_USE_POLL
#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
#endif

#else // not _WIN32

#include <arpa/inet.h>
#include <cstring>
#include <ifaddrs.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef CPPHTTPLIB_USE_POLL
#include <poll.h>
#endif
#include <csignal>
#include <pthread.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>

using socket_t = int;
#define INVALID_SOCKET (-1)
#endif //_WIN32

#include <array>
#include <atomic>
#include <cassert>
#include <climits>
#include <condition_variable>
#include <errno.h>
#include <fcntl.h>
#include <fstream>
#include <functional>
#include <list>
#include <map>
#include <memory>
#include <mutex>
#include <random>
#include <regex>
#include <string>
#include <sys/stat.h>
#include <thread>
#include <utility>

#define CPPHTTPLIB_OPENSSL_SUPPORT 1

#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
#ifdef WIN32
#include <Windows.h>
#endif
#include <openssl/err.h>
#include <openssl/md5.h>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>

#include <iomanip>
#include <iostream>
#include <sstream>

// #if OPENSSL_VERSION_NUMBER < 0x1010100fL
// #error Sorry, OpenSSL versions prior to 1.1.1 are not supported
// #endif

#if OPENSSL_VERSION_NUMBER < 0x10100000L
#include <openssl/crypto.h>
inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) {
  return M_ASN1_STRING_data(asn1);
}
#endif
#endif

#ifdef CPPHTTPLIB_ZLIB_SUPPORT
#include <zlib.h>
#endif

#include <jebase/config.h>
#include "QLogger.h"
#include <cross/SystemUtil.h>
/*
 * Declaration
 */
namespace httplib {

namespace detail {

struct ci {
  bool operator()(const std::string &s1, const std::string &s2) const {
    return std::lexicographical_compare(
        s1.begin(), s1.end(), s2.begin(), s2.end(),
        [](char c1, char c2) { return ::tolower(c1) < ::tolower(c2); });
  }
};

} // namespace detail

using Headers = std::multimap<std::string, std::string, detail::ci>;

using Params = std::multimap<std::string, std::string>;
using Match = std::smatch;

using Progress = std::function<bool(uint64_t current, uint64_t total)>;

struct Response;
using ResponseHandler = std::function<bool(const Response &response)>;

struct MultipartFormData {
  std::string name;
  std::string content;
  std::string filename;
  std::string content_type;
};
using MultipartFormDataItems = std::vector<MultipartFormData>;
using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;

class JE_API DataSink {
public:
  DataSink() = default;
  DataSink(const DataSink &) = delete;
  DataSink &operator=(const DataSink &) = delete;
  DataSink(DataSink &&) = delete;
  DataSink &operator=(DataSink &&) = delete;

  std::function<void(const char *data, size_t data_len)> write;
  std::function<void()> done;
  std::function<bool()> is_writable;
};

using ContentProvider =
    std::function<void(size_t offset, size_t length, DataSink &sink)>;

using ContentReceiver =
    std::function<bool(const char *data, size_t data_length)>;

using MultipartContentHeader =
    std::function<bool(const MultipartFormData &file)>;

class JE_API ContentReader {
public:
  using Reader = std::function<bool(ContentReceiver receiver)>;
  using MultipartReader = std::function<bool(MultipartContentHeader header,
                                             ContentReceiver receiver)>;

  ContentReader(Reader reader, MultipartReader muitlpart_reader)
      : reader_(std::move(reader)), muitlpart_reader_(std::move(muitlpart_reader)) {}

  bool operator()(MultipartContentHeader header,
                  ContentReceiver receiver) const {
    return muitlpart_reader_(std::move(header), std::move(receiver));
  }

  bool operator()(ContentReceiver receiver) const { return reader_(std::move(receiver)); }

  Reader reader_;
  MultipartReader muitlpart_reader_;
};

using Range = std::pair<ssize_t, ssize_t>;
using Ranges = std::vector<Range>;

struct JE_API Request {
  std::string method;
  std::string path;
  Headers headers;
  std::string body;

  std::string remote_addr;
  int remote_port = -1;

  // for server
  std::string version;
  std::string target;
  Params params;
  MultipartFormDataMap files;
  Ranges ranges;
  Match matches;

  // for client
  size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT;
  ResponseHandler response_handler;
  ContentReceiver content_receiver;
  Progress progress;

#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
  const SSL *ssl;
#endif

  bool has_header(const char *key) const;
  std::string get_header_value(const char *key, size_t id = 0) const;
  size_t get_header_value_count(const char *key) const;
  void set_header(const char *key, const char *val);
  void set_header(const char *key, const std::string &val);

  bool has_param(const char *key) const;
  std::string get_param_value(const char *key, size_t id = 0) const;
  size_t get_param_value_count(const char *key) const;

  bool is_multipart_form_data() const;

  bool has_file(const char *key) const;
  MultipartFormData get_file_value(const char *key) const;

  // private members...
  size_t content_length{};
  ContentProvider content_provider;
};

struct JE_API Response {
  std::string version;
  int status = -1;
  Headers headers;
  std::string body;

  bool has_header(const char *key) const;
  std::string get_header_value(const char *key, size_t id = 0) const;
  size_t get_header_value_count(const char *key) const;
  void set_header(const char *key, const char *val);
  void set_header(const char *key, const std::string &val);

  void set_redirect(const char *url, int status = 302);
  void set_content(const char *s, size_t n, const char *content_type);
  void set_content(std::string s, const char *content_type);

  void set_content_provider(
      size_t length,
      std::function<void(size_t offset, size_t length, DataSink &sink)>
          provider,
      std::function<void()> resource_releaser = [] {});

  void set_chunked_content_provider(
      std::function<void(size_t offset, DataSink &sink)> provider,
      std::function<void()> resource_releaser = [] {});

  Response() = default;
  Response(const Response &) = default;
  Response &operator=(const Response &) = default;
  Response(Response &&) = default;
  Response &operator=(Response &&) = default;
  ~Response() {
    if (content_provider_resource_releaser) {
      content_provider_resource_releaser();
    }
  }

  // private members...
  size_t content_length = 0;
  ContentProvider content_provider;
  std::function<void()> content_provider_resource_releaser;
};

class JE_API Stream {
public:
  virtual ~Stream() = default;

  virtual bool is_readable() const = 0;
  virtual bool is_writable() const = 0;

  virtual ssize_t read(char *ptr, size_t size) = 0;
  virtual ssize_t write(const char *ptr, size_t size) = 0;
  virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0;

  template <typename... Args>
  ssize_t write_format(const char *fmt, const Args &... args);
  ssize_t write(const char *ptr);
  ssize_t write(const std::string &s);
};

class JE_API TaskQueue {
public:
  TaskQueue() = default;
  virtual ~TaskQueue() = default;

  virtual void increase(size_t n) = 0;          //添加线程个数

  virtual void enqueue(const std::function<void()>& fn, const std::string& disc="") = 0;  //描述

  virtual void shutdown() = 0;

  virtual void on_idle(){};
};

//退出自检
class JE_API ThreadPool : public TaskQueue {
public:
    explicit ThreadPool(size_t n, size_t max = 32, size_t pool_overload = 32);

    ThreadPool(const ThreadPool &) = delete;
    ~ThreadPool() override = default;

    void increase(size_t n) override;

    void enqueue(const std::function<void()>& fn, const std::string& disc="") override;

    void shutdown() override;

    void showThreadsStatus(std::string* result = nullptr);

    void resetMaxThreadCount(size_t count) {
        m_ = count;
    }

    inline bool isPrepared() {
        return r_ > 0;
    }

    void resetPool();

    inline void resetPoolOverload(size_t overload) {
        if(overload<32) overload = 32;
        safe_check_ = overload;
    }

    int getMaxThreadCounts();

private:
    struct pl_func {

        explicit pl_func(std::function<void(void)>  func, std::string  disc, unsigned long tm) :
                fn_(std::move(func)), disc_(std::move(disc)), in_(tm) {
        }
        std::function<void()> fn_;
        std::string disc_;   //source,line,file
        unsigned long in_;
        friend ThreadPool;
    };

  struct worker {
    explicit worker(ThreadPool &pool) : pool_(pool) {}
    void operator()();
    ThreadPool &pool_;
  };
  size_t m_;
  size_t init_n_;
  size_t safe_check_;
  friend struct worker;
  std::vector<std::thread> threads_;
  std::list<pl_func*> jobs_;
  unsigned long r_ = 0; //总线程数量
  unsigned long b_ = 0; //空闲线程数量
  bool shutdown_;
  std::condition_variable cond_;
  std::recursive_mutex access_mutex_;
  std::mutex work_notify_mutex_;

};
using RThreadPool = std::shared_ptr<ThreadPool>;
using Logger = std::function<void(const Request &, const Response &)>;

class JE_API Server {
public:
  using Handler = std::function<void(const Request &, Response &)>;
  using HandlerWithContentReader = std::function<void(
      const Request &, Response &, const ContentReader &content_reader)>;
  using Expect100ContinueHandler =
      std::function<int(const Request &, Response &)>;

  Server(httplib::ThreadPool& pool);

  virtual ~Server();

  virtual bool is_valid() const;

  Server &Get(const char *pattern, Handler handler);
  Server &Post(const char *pattern, Handler handler);
  Server &Post(const char *pattern, HandlerWithContentReader handler);
  Server &Put(const char *pattern, Handler handler);
  Server &Put(const char *pattern, HandlerWithContentReader handler);
  Server &Patch(const char *pattern, Handler handler);
  Server &Patch(const char *pattern, HandlerWithContentReader handler);
  Server &Delete(const char *pattern, Handler handler);
  Server &Delete(const char *pattern, HandlerWithContentReader handler);
  Server &Options(const char *pattern, Handler handler);

  [[deprecated]] bool set_base_dir(const char *dir,
                                   const char *mount_point = nullptr);
  bool set_mount_point(const char *mount_point, const char *dir);
  bool remove_mount_point(const char *mount_point);
  void set_file_extension_and_mimetype_mapping(const char *ext,
                                               const char *mime);
  void set_file_request_handler(Handler handler);

  void set_error_handler(Handler handler);
  void set_logger(Logger logger);

  void set_expect_100_continue_handler(Expect100ContinueHandler handler);

  void set_keep_alive_max_count(size_t count);
  void set_read_timeout(time_t sec, time_t usec);
  void set_payload_max_length(size_t length);

  bool bind_to_port(const char *host, int port, int socket_flags = 0);
  int bind_to_any_port(const char *host, int socket_flags = 0);
  bool listen_after_bind();

  bool listen(const char *host, int port, int socket_flags = 0);

  bool is_running() const;
  void stop();

//  std::function<TaskQueue *(void)> new_task_queue;

    void increaseTaskQueueThread(unsigned long poolSize);

    bool listenOnNewThread(const std::vector<std::string> &hostList, int port);

    bool listenOnNewThread(const std::string& host, int port);

protected:
  bool process_request(Stream &strm, bool last_connection,
                       bool &connection_close,
                       const std::function<void(Request &)> &setup_request);

  size_t keep_alive_max_count_;
  time_t read_timeout_sec_;
  time_t read_timeout_usec_;
  size_t payload_max_length_;

  TaskQueue * task_queue_;

private:
  using Handlers = std::vector<std::pair<std::string, Handler>>;
  using HandlersForContentReader =
      std::vector<std::pair<std::string, HandlerWithContentReader>>;

  socket_t create_server_socket(const std::string& host, int port,
                                int socket_flags) const;
  int bind_internal(socket_t& srvsock, const std::string& host, int port, int socket_flags);

  bool listen_internal(const socket_t& srvsock);

  bool routing(Request &req, Response &res, Stream &strm);
  bool handle_file_request(Request &req, Response &res, bool head = false);
  bool dispatch_request(Request &req, Response &res, Handlers &handlers);
  bool dispatch_request_for_content_reader(Request &req, Response &res,
                                           const ContentReader& content_reader,
                                           HandlersForContentReader &handlers);

  bool parse_request_line(const char *s, Request &req);
  bool write_response(Stream &strm, bool last_connection, const Request &req,
                      Response &res);
  bool write_content_with_provider(Stream &strm, const Request &req,
                                   Response &res, const std::string &boundary,
                                   const std::string &content_type);
  bool read_content(Stream &strm, Request &req, Response &res);
  bool
  read_content_with_content_receiver(Stream &strm, Request &req, Response &res,
                                     ContentReceiver receiver,
                                     MultipartContentHeader multipart_header,
                                     ContentReceiver multipart_receiver);
  bool read_content_core(Stream &strm, Request &req, Response &res,
                         ContentReceiver receiver,
                         MultipartContentHeader mulitpart_header,
                         ContentReceiver multipart_receiver);

  virtual bool process_and_close_socket(socket_t sock);

  std::atomic<bool> is_running_;
  std::atomic<int>  multi_running_;
  std::atomic<socket_t> svr_sock_;
  std::vector<socket_t> multi_socks_;
  std::mutex            sock_mutex_;
  std::vector<std::pair<std::string, std::string>> base_dirs_;
  std::map<std::string, std::string> file_extension_and_mimetype_map_;
  Handler file_request_handler_;
  Handlers get_handlers_;
  Handlers post_handlers_;
  HandlersForContentReader post_handlers_for_content_reader_;
  Handlers put_handlers_;
  HandlersForContentReader put_handlers_for_content_reader_;
  Handlers patch_handlers_;
  HandlersForContentReader patch_handlers_for_content_reader_;
  Handlers delete_handlers_;
  HandlersForContentReader delete_handlers_for_content_reader_;
  Handlers options_handlers_;
  Handler error_handler_;
  Logger logger_;
  Expect100ContinueHandler expect_100_continue_handler_;

        void start_listen_on_sock(const socket_t &oksock);

        void notify_listen_end();
    };

class JE_API Client {
public:
  explicit Client(const std::string &host, int port = 80,
                  const std::string &client_cert_path = std::string(),
                  const std::string &client_key_path = std::string());

  virtual ~Client();

  virtual bool is_valid() const;

  inline const std::string& host() { return host_; }

  inline int port() { return port_; }

  std::shared_ptr<Response> Get(const char *path);

  std::shared_ptr<Response> Get(const char *path, const Headers &headers);

  std::shared_ptr<Response> Get(const char *path, Progress progress);

  std::shared_ptr<Response> Get(const char *path, const Headers &headers,
                                Progress progress);

  std::shared_ptr<Response> Get(const char *path,
                                ContentReceiver content_receiver);

  std::shared_ptr<Response> Get(const char *path, const Headers &headers,
                                ContentReceiver content_receiver);

  std::shared_ptr<Response>
  Get(const char *path, ContentReceiver content_receiver, Progress progress);

  std::shared_ptr<Response> Get(const char *path, const Headers &headers,
                                ContentReceiver content_receiver,
                                Progress progress);

  std::shared_ptr<Response> Get(const char *path, const Headers &headers,
                                ResponseHandler response_handler,
                                ContentReceiver content_receiver);

  std::shared_ptr<Response> Get(const char *path, const Headers &headers,
                                ResponseHandler response_handler,
                                ContentReceiver content_receiver,
                                Progress progress);

  std::shared_ptr<Response> Head(const char *path);

  std::shared_ptr<Response> Head(const char *path, const Headers &headers);

  std::shared_ptr<Response> Post(const char *path);

  std::shared_ptr<Response> Post(const char *path, const std::string &body,
                                 const char *content_type);

  std::shared_ptr<Response> Post(const char *path, const Headers &headers,
                                 const std::string &body,
                                 const char *content_type);

  std::shared_ptr<Response> Post(const char *path, size_t content_length,
                                 ContentProvider content_provider,
                                 const char *content_type);

  std::shared_ptr<Response> Post(const char *path, const Headers &headers,
                                 size_t content_length,
                                 ContentProvider content_provider,
                                 const char *content_type);

  std::shared_ptr<Response> Post(const char *path, const Params &params);

  std::shared_ptr<Response> Post(const char *path, const Headers &headers,
                                 const Params &params);

  std::shared_ptr<Response> Post(const char *path,
                                 const MultipartFormDataItems &items);

  std::shared_ptr<Response> Post(const char *path, const Headers &headers,
                                 const MultipartFormDataItems &items);

  std::shared_ptr<Response> Put(const char *path);

  std::shared_ptr<Response> Put(const char *path, const std::string &body,
                                const char *content_type);

  std::shared_ptr<Response> Put(const char *path, const Headers &headers,
                                const std::string &body,
                                const char *content_type);

  std::shared_ptr<Response> Put(const char *path, size_t content_length,
                                ContentProvider content_provider,
                                const char *content_type);

  std::shared_ptr<Response> Put(const char *path, const Headers &headers,
                                size_t content_length,
                                ContentProvider content_provider,
                                const char *content_type);

  std::shared_ptr<Response> Put(const char *path, const Params &params);

  std::shared_ptr<Response> Put(const char *path, const Headers &headers,
                                const Params &params);

  std::shared_ptr<Response> Patch(const char *path, const std::string &body,
                                  const char *content_type);

  std::shared_ptr<Response> Patch(const char *path, const Headers &headers,
                                  const std::string &body,
                                  const char *content_type);

  std::shared_ptr<Response> Patch(const char *path, size_t content_length,
                                  ContentProvider content_provider,
                                  const char *content_type);

  std::shared_ptr<Response> Patch(const char *path, const Headers &headers,
                                  size_t content_length,
                                  ContentProvider content_provider,
                                  const char *content_type);

  std::shared_ptr<Response> Delete(const char *path);

  std::shared_ptr<Response> Delete(const char *path, const std::string &body,
                                   const char *content_type);

  std::shared_ptr<Response> Delete(const char *path, const Headers &headers);

  std::shared_ptr<Response> Delete(const char *path, const Headers &headers,
                                   const std::string &body,
                                   const char *content_type);

  std::shared_ptr<Response> Options(const char *path);

  std::shared_ptr<Response> Options(const char *path, const Headers &headers);

  bool send(const Request &req, Response &res);

  bool send(const std::vector<Request> &requests,
            std::vector<Response> &responses);

  void stop();

  void set_timeout_sec(time_t timeout_sec);

  void set_read_timeout(time_t sec, time_t usec);

  void set_keep_alive_max_count(size_t count);

  void set_basic_auth(const char *username, const char *password);

#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
  void set_digest_auth(const char *username, const char *password);
#endif

  void set_follow_location(bool on);

  void set_compress(bool on);

  void set_interface(const char *intf);

  void set_proxy(const char *host, int port);

  void set_proxy_basic_auth(const char *username, const char *password);

#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
  void set_proxy_digest_auth(const char *username, const char *password);
#endif

  void set_logger(Logger logger);

  bool checkTtlTimeout() {
      return (--ttl_)<1;
  }

  void resetTtlTimeout() {
      ttl_ = 10;
  }

protected:
  bool process_request(Stream &strm, const Request &req, Response &res,
                       bool last_connection, bool &connection_close);

  std::atomic<socket_t> sock_;

  const std::string host_;
  const int port_;
  const std::string host_and_port_;
  int ttl_ = 10;    //ten mins.

  // Settings
  std::string client_cert_path_;
  std::string client_key_path_;

  time_t timeout_sec_ = 300;
  time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;
  time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND;

  size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;

  std::string basic_auth_username_;
  std::string basic_auth_password_;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
  std::string digest_auth_username_;
  std::string digest_auth_password_;
#endif

  bool follow_location_ = false;

  bool compress_ = false;

  std::string interface_;

  std::string proxy_host_;
  int proxy_port_;

  std::string proxy_basic_auth_username_;
  std::string proxy_basic_auth_password_;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
  std::string proxy_digest_auth_username_;
  std::string proxy_digest_auth_password_;
#endif

  Logger logger_;

  void copy_settings(const Client &rhs) {
    client_cert_path_ = rhs.client_cert_path_;
    client_key_path_ = rhs.client_key_path_;
    timeout_sec_ = rhs.timeout_sec_;
    read_timeout_sec_ = rhs.read_timeout_sec_;
    read_timeout_usec_ = rhs.read_timeout_usec_;
    keep_alive_max_count_ = rhs.keep_alive_max_count_;
    basic_auth_username_ = rhs.basic_auth_username_;
    basic_auth_password_ = rhs.basic_auth_password_;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    digest_auth_username_ = rhs.digest_auth_username_;
    digest_auth_password_ = rhs.digest_auth_password_;
#endif
    follow_location_ = rhs.follow_location_;
    compress_ = rhs.compress_;
    interface_ = rhs.interface_;
    proxy_host_ = rhs.proxy_host_;
    proxy_port_ = rhs.proxy_port_;
    proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
    proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
    proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
    proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
#endif
    logger_ = rhs.logger_;
  }

private:
  socket_t create_client_socket() const;
  bool read_response_line(Stream &strm, Response &res);
  bool write_request(Stream &strm, const Request &req, bool last_connection);
  bool redirect(const Request &req, Response &res);
  bool handle_request(Stream &strm, const Request &req, Response &res,
                      bool last_connection, bool &connection_close);
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
  bool connect(socket_t sock, Response &res, bool &error);
#endif

  std::shared_ptr<Response> send_with_content_provider(
      const char *method, const char *path, const Headers &headers,
      const std::string &body, size_t content_length,
      ContentProvider content_provider, const char *content_type);

  virtual bool process_and_close_socket(
      socket_t sock, size_t request_count,
      std::function<bool(Stream &strm, bool last_connection,
                         bool &connection_close)>
          callback);

  virtual bool is_ssl() const;
};

inline void Get(std::vector<Request> &requests, const char *path,
                const Headers &headers) {
  Request req;
  req.method = "GET";
  req.path = path;
  req.headers = headers;
  requests.emplace_back(std::move(req));
}

inline void Get(std::vector<Request> &requests, const char *path) {
  Get(requests, path, Headers());
}

inline void Post(std::vector<Request> &requests, const char *path,
                 const Headers &headers, const std::string &body,
                 const char *content_type) {
  Request req;
  req.method = "POST";
  req.path = path;
  req.headers = headers;
  if (content_type) { req.headers.emplace("Content-Type", content_type); }
  req.body = body;
  requests.emplace_back(std::move(req));
}

inline void Post(std::vector<Request> &requests, const char *path,
                 const std::string &body, const char *content_type) {
  Post(requests, path, Headers(), body, content_type);
}

#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
class JE_API SSLServer : public Server {
public:
  SSLServer(httplib::ThreadPool& pool,
            const char *cert_path, const char *private_key_path,
            const char *client_ca_cert_file_path = nullptr,
            const char *client_ca_cert_dir_path = nullptr);

  SSLServer(httplib::ThreadPool& pool,
            X509 *cert, EVP_PKEY *private_key,
            X509_STORE *client_ca_cert_store = nullptr);

  ~SSLServer() override;

  bool is_valid() const override;

private:
  bool process_and_close_socket(socket_t sock) override;

  SSL_CTX *ctx_;
  std::mutex ctx_mutex_;
};

class JE_API SSLClient : public Client {
public:
  explicit SSLClient(const std::string &host, int port = 443,
                     const std::string &client_cert_path = std::string(),
                     const std::string &client_key_path = std::string());

  SSLClient(const std::string &host, int port, X509 *client_cert,
            EVP_PKEY *client_key);

  ~SSLClient() override;

  bool is_valid() const override;

  void set_ca_cert_path(const char *ca_ceert_file_path,
                        const char *ca_cert_dir_path = nullptr);

  void set_ca_cert_store(X509_STORE *ca_cert_store);

  void enable_server_certificate_verification(bool enabled);

  long get_openssl_verify_result() const;

  SSL_CTX *ssl_context() const;

private:
  bool process_and_close_socket(
      socket_t sock, size_t request_count,
      std::function<bool(Stream &strm, bool last_connection,
                         bool &connection_close)>
          callback) override;
  bool is_ssl() const override;

  bool verify_host(X509 *server_cert) const;
  bool verify_host_with_subject_alt_name(X509 *server_cert) const;
  bool verify_host_with_common_name(X509 *server_cert) const;
  bool check_host_name(const char *pattern, size_t pattern_len) const;

  SSL_CTX *ctx_;
  std::mutex ctx_mutex_;
  std::vector<std::string> host_components_;

  std::string ca_cert_file_path_;
  std::string ca_cert_dir_path_;
  X509_STORE *ca_cert_store_ = nullptr;
  bool server_certificate_verification_ = false;
  long verify_result_ = 0;
};
#endif

namespace url {

    struct Options {
        // TODO: support more options...
        bool follow_location = false;
        std::string client_cert_path;
        std::string client_key_path;

        std::string ca_cert_file_path;
        std::string ca_cert_dir_path;
        bool server_certificate_verification = false;
    };

    std::shared_ptr<Response> Get(const char *url, Options &options);

    std::shared_ptr<Response> Get(const char *url);
}




namespace detail {

    class JE_API SocketStream : public Stream {
    public:
        SocketStream(socket_t sock, time_t read_timeout_sec,
                     time_t read_timeout_usec);
        ~SocketStream() override;

        bool is_readable() const override;
        bool is_writable() const override;
        ssize_t read(char *ptr, size_t size) override;
        ssize_t write(const char *ptr, size_t size) override;
        void get_remote_ip_and_port(std::string &ip, int &port) const override;
    private:
        socket_t sock_;
        time_t read_timeout_sec_;
        time_t read_timeout_usec_;
    };

    class JE_API BufferStream : public Stream {
    public:
        BufferStream() = default;
        ~BufferStream() override = default;

        bool is_readable() const override;
        bool is_writable() const override;
        ssize_t read(char *ptr, size_t size) override;
        ssize_t write(const char *ptr, size_t size) override;
        void get_remote_ip_and_port(std::string &ip, int &port) const override;

        const std::string &get_buffer() const;

    private:
        std::string buffer;
        size_t position = 0;
    };


#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
        class SSLSocketStream : public Stream {
public:
  SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec,
                  time_t read_timeout_usec);
  ~SSLSocketStream() override;

  bool is_readable() const override;
  bool is_writable() const override;
  ssize_t read(char *ptr, size_t size) override;
  ssize_t write(const char *ptr, size_t size) override;
  void get_remote_ip_and_port(std::string &ip, int &port) const override;

private:
  socket_t sock_;
  SSL *ssl_;
  time_t read_timeout_sec_;
  time_t read_timeout_usec_;
};
#endif

        JE_API int close_socket(socket_t sock);

        JE_API int select_read(socket_t sock, time_t sec, time_t usec);

        JE_API int select_write(socket_t sock, time_t sec, time_t usec);

        JE_API bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec);

        JE_API int shutdown_socket(socket_t sock);

        JE_API void set_nonblocking(socket_t sock, bool nonblocking);

        JE_API bool is_connection_error();

        JE_API bool bind_ip_address(socket_t sock, const char *host);

        JE_API std::string if2ip(const std::string &ifn);


        JE_API socket_t create_client_socket(const char *host, int port,
                                             time_t timeout_sec,
                                             const std::string &intf);


        JE_API void get_remote_ip_and_port(const struct sockaddr_storage &addr,
                                    socklen_t addr_len, std::string &ip,
                                    int &port);

        JE_API void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port);

        JE_API const char *
        find_content_type(const std::string &path,
                          const std::map<std::string, std::string> &user_data);

        JE_API const char *status_message(int status);

        JE_API std::string encode_url(const std::string &s);

        JE_API std::string decode_url(const std::string &s,
                                   bool convert_plus_to_space);

        JE_API int select_read(socket_t sock, time_t sec, time_t usec);

        JE_API bool is_hex(char c, int &v);

        JE_API bool from_hex_to_i(const std::string &s, size_t i, size_t cnt,
                       int &val);

        JE_API std::string from_i_to_hex(size_t n);

        JE_API size_t to_utf8(int code, char *buff);

        JE_API std::string base64_encode(const std::string &in);

        JE_API bool is_file(const std::string &path);

        JE_API bool is_dir(const std::string &path);

        JE_API bool is_valid_path(const std::string &path);

        JE_API void read_file(const std::string &path, std::string &out);

        template <class Fn> void split(const char *b, const char *e, char d, Fn fn);

        JE_API std::string to_lower(const char *beg, const char *end);

        template <typename Fn> socket_t create_socket(const char *host, int port, Fn fn,
                               int socket_flags = 0) {
#ifdef _WIN32
            #define SO_SYNCHRONOUS_NONALERT 0x20
#define SO_OPENTYPE 0x7008

  int opt = SO_SYNCHRONOUS_NONALERT;
  setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&opt,
             sizeof(opt));
#endif

            // Get address info
            struct addrinfo hints;
            struct addrinfo *result;

            memset(&hints, 0, sizeof(struct addrinfo));
            hints.ai_family = AF_UNSPEC;
            hints.ai_socktype = SOCK_STREAM;
            hints.ai_flags = socket_flags;
            hints.ai_protocol = 0;

            auto service = std::to_string(port);

            if (getaddrinfo(host, service.c_str(), &hints, &result)) {
                return INVALID_SOCKET;
            }

            for (auto rp = result; rp; rp = rp->ai_next) {
                // Create a socket
#ifdef _WIN32
                auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol,
                           nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT);
    /**
     * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1
     * and above the socket creation fails on older Windows Systems.
     *
     * Let's try to create a socket the old way in this case.
     *
     * Reference:
     * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa
     *
     * WSA_FLAG_NO_HANDLE_INHERIT:
     * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with
     * SP1, and later
     *
     */
    if (sock == INVALID_SOCKET) {
      sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
    }
#else
                auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
#endif
                if (sock == INVALID_SOCKET) { continue; }

#ifndef _WIN32
                if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { continue; }
#endif

                // Make 'reuse address' option available
                int yes = 1;
                setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&yes),
                           sizeof(yes));
#ifdef SO_REUSEPORT
                setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<char *>(&yes),
               sizeof(yes));
#endif

                // bind or connect
                if (fn(sock, *rp)) {
                    freeaddrinfo(result);
                    return sock;
                }

                close_socket(sock);
            }

            freeaddrinfo(result);
            return INVALID_SOCKET;
        }
}   //detail

namespace url {
    std::shared_ptr<Response> Get(const char *url, Options &options);
    std::shared_ptr<Response> Get(const char *url);
}


} // namespace httplib

#endif // CPPHTTPLIB_HTTPLIB_H

