Shankusu993 / izanami

izanami -she who invites
MIT License
4 stars 3 forks source link

Create Chatroom Server #1

Open Shankusu993 opened 4 years ago

Shankusu993 commented 4 years ago

Write server script with following things in mind:

KIRA009 commented 4 years ago

Is there a reason for the server to be written in cpp and not Python?

Shankusu993 commented 4 years ago

Hi there @KIRA009 , there was no particular reason apart from the fact that c++ makes processing faster, and we want our server to be fast right? but since we don't know how many users to expect just yet so we kept it open and you can suggest the changes in tech stack where you feel necessary in the #chat-service in the slack workspace.

hk45 commented 4 years ago

struct Response { std::string version; int status; 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); void set_content(const char s, size_t n, const char content_type); void set_content(const 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, Done done)> provider, std::function<void()> resource_releaser = [] {});

Response() : status(-1), content_length(0) {}

~Response() { if (content_provider_resource_releaser) { content_provider_resource_releaser(); } }

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

class Stream { public: virtual ~Stream() = default; virtual int read(char ptr, size_t size) = 0; virtual int write(const char ptr, size_t size1) = 0; virtual int write(const char *ptr) = 0; virtual int write(const std::string &s) = 0; virtual std::string get_remote_addr() const = 0;

template int write_format(const char *fmt, const Args &... args); };

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

int read(char ptr, size_t size) override; int write(const char ptr, size_t size) override; int write(const char *ptr) override; int write(const std::string &s) override; std::string get_remote_addr() const override;

private: sockett sock; time_t read_timeoutsec; time_t read_timeoutusec; };

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

int read(char ptr, size_t size) override; int write(const char ptr, size_t size) override; int write(const char *ptr) override; int write(const std::string &s) override; std::string get_remote_addr() const override;

const std::string &get_buffer() const;

private: std::string buffer; };

class TaskQueue { public: TaskQueue() = default; virtual ~TaskQueue() = default; virtual void enqueue(std::function<void()> fn) = 0; virtual void shutdown() = 0; };

if CPPHTTPLIB_THREAD_POOL_COUNT > 0

class ThreadPool : public TaskQueue { public: explicit ThreadPool(sizet n) : shutdown(false) { while (n) { threads_.emplace_back(worker(*this)); n--; } }

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

void enqueue(std::function<void()> fn) override { std::uniquelock lock(mutex); jobs_.pushback(fn); cond.notify_one(); }

void shutdown() override { // Stop all worker threads... { std::uniquelock lock(mutex); shutdown_ = true; }

cond_.notify_all();

// Join...
for (auto &t : threads_) {
  t.join();
}

}

private: struct worker { explicit worker(ThreadPool &pool) : pool_(pool) {}

void operator()() {
  for (;;) {
    std::function<void()> fn;
    {
      std::unique_lock<std::mutex> lock(pool_.mutex_);

      pool_.cond_.wait(
          lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; });

      if (pool_.shutdown_ && pool_.jobs_.empty()) { break; }

      fn = pool_.jobs_.front();
      pool_.jobs_.pop_front();
    }

    assert(true == static_cast<bool>(fn));
    fn();
  }
}

ThreadPool &pool_;

}; friend struct worker;

std::vector threads; std::list<std::function<void()>> jobs;

bool shutdown_;

std::conditionvariable cond; std::mutex mutex_; };

elif CPPHTTPLIB_THREAD_POOL_COUNT == 0

class Threads : public TaskQueue { public: Threads() : runningthreads(0) {} virtual ~Threads() {}

virtual void enqueue(std::function<void()> fn) override { std::thread([=]() { { std::lock_guard guard(running_threadsmutex); runningthreads++; }

  fn();

  {
    std::lock_guard<std::mutex> guard(running_threads_mutex_);
    running_threads_--;
  }
}).detach();

}

virtual void shutdown() override { for (;;) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::lock_guard guard(running_threadsmutex); if (!runningthreads) { break; } } }

private: std::mutex running_threadsmutex; int runningthreads; };

else

class NoThread : public TaskQueue { public: NoThread() {} virtual ~NoThread() {}

virtual void enqueue(std::function<void()> fn) override { fn(); }

virtual void shutdown() override {} };

endif

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

Server();

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 &Options(const char *pattern, Handler handler);

bool set_base_dir(const char dir, const char mount_point = nullptr); void set_file_request_handler(Handler handler);

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

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;

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

size_t keep_alive_maxcount; time_t read_timeoutsec; time_t read_timeoutusec; size_t payload_maxlength;

private: using Handlers = std::vector<std::pair<std::regex, Handler>>; using HandersForContentReader = std::vector<std::pair<std::regex, HandlerWithContentReader>>;

socket_t create_server_socket(const char host, int port, int socket_flags) const; int bind_internal(const char host, int port, int socket_flags); bool listen_internal();

bool routing(Request &req, Response &res, Stream &strm, bool last_connection); bool handle_file_request(Request &req, Response &res); bool dispatch_request(Request &req, Response &res, Handlers &handlers); bool dispatch_request_for_content_reader(Request &req, Response &res, ContentReader content_reader, HandersForContentReader &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, bool last_connection, Request &req, Response &res); bool read_content_with_content_receiver(Stream &strm, bool last_connection, Request &req, Response &res, ContentReceiver receiver, MultipartContentHeader multipart_header, MultipartContentReceiver multipart_receiver); bool read_content_core(Stream &strm, bool last_connection, Request &req, Response &res, ContentReceiver receiver, MultipartContentHeader mulitpart_header, MultipartContentReceiver multipart_receiver);

virtual bool process_and_close_socket(socket_t sock);

std::atomic isrunning; std::atomic svrsock; std::vector<std::pair<std::string, std::string>> basedirs; Handler file_requesthandler; Handlers gethandlers; Handlers posthandlers; HandersForContentReader post_handlers_for_contentreader; Handlers puthandlers; HandersForContentReader put_handlers_for_contentreader; Handlers patchhandlers; HandersForContentReader patch_handlers_for_contentreader; Handlers deletehandlers; Handlers optionshandlers; Handler errorhandler; Logger logger_; };

class Client { public: explicit Client(const char *host, int port = 80, time_t timeout_sec = 300);

virtual ~Client();

virtual bool is_valid() const;

std::shared_ptr Get(const char *path);

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

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

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

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

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

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

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

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

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

std::shared_ptr Head(const char *path);

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

std::shared_ptr Post(const char path, const std::string &body, const char content_type, bool compress = false);

std::shared_ptr Post(const char path, const Headers &headers, const std::string &body, const char content_type, bool compress = false);

std::shared_ptr Post(const char path, size_t content_length, ContentProvider content_provider, const char content_type, bool compress = false);

std::shared_ptr Post(const char path, const Headers &headers, size_t content_length, ContentProvider content_provider, const char content_type, bool compress = false);

std::shared_ptr Post(const char *path, const Params &params, bool compress = false);

std::shared_ptr Post(const char *path, const Headers &headers, const Params &params, bool compress = false);

std::shared_ptr Post(const char *path, const MultipartFormDataItems &items, bool compress = false);

std::shared_ptr Post(const char *path, const Headers &headers, const MultipartFormDataItems &items, bool compress = false);

std::shared_ptr Put(const char path, const std::string &body, const char content_type, bool compress = false);

std::shared_ptr Put(const char path, const Headers &headers, const std::string &body, const char content_type, bool compress = false);

std::shared_ptr Put(const char path, size_t content_length, ContentProvider content_provider, const char content_type, bool compress = false);

std::shared_ptr Put(const char path, const Headers &headers, size_t content_length, ContentProvider content_provider, const char content_type, bool compress = false);

std::shared_ptr Patch(const char path, const std::string &body, const char content_type, bool compress = false);

std::shared_ptr Patch(const char path, const Headers &headers, const std::string &body, const char content_type, bool compress = false);

std::shared_ptr Patch(const char path, size_t content_length, ContentProvider content_provider, const char content_type, bool compress = false);

std::shared_ptr Patch(const char path, const Headers &headers, size_t content_length, ContentProvider content_provider, const char content_type, bool compress = false);

std::shared_ptr Delete(const char *path);

std::shared_ptr Delete(const char path, const std::string &body, const char content_type);

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

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

std::shared_ptr Options(const char *path);

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

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

bool send(const std::vector &requests, std::vector &responses);

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

void follow_location(bool on);

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

const std::string host; const int port; time_t timeoutsec; const std::string host_andport; size_t keep_alive_maxcount; time_t read_timeoutsec; time_t read_timeoutusec; size_t followlocation;

private: socket_t create_client_socket() const; bool read_response_line(Stream &strm, Response &res); void write_request(Stream &strm, const Request &req, bool last_connection); bool redirect(const Request &req, Response &res);

std::shared_ptr 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, bool compress);

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 &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 &requests, const char *path) { Get(requests, path, Headers()); }

inline void Post(std::vector &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; req.headers.emplace("Content-Type", content_type); req.body = body; requests.emplace_back(std::move(req)); }

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

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); virtual ~SSLSocketStream();

virtual int read(char ptr, size_t size); virtual int write(const char ptr, size_t size); virtual int write(const char *ptr); virtual int write(const std::string &s); virtual std::string get_remote_addr() const;

private: sockett sock; SSL *ssl_; time_t read_timeoutsec; time_t read_timeoutusec; };

class SSLServer : public Server { public: SSLServer(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);

virtual ~SSLServer();

virtual bool is_valid() const;

private: virtual bool process_and_close_socket(socket_t sock);

SSLCTX *ctx; std::mutex ctxmutex; };

class SSLClient : public Client { public: SSLClient(const char host, int port = 443, time_t timeout_sec = 300, const char client_cert_path = nullptr, const char *client_key_path = nullptr);

virtual ~SSLClient();

virtual bool is_valid() const;

void set_ca_cert_path(const char ca_ceert_file_path, const char ca_cert_dir_path = nullptr); void enable_server_certificate_verification(bool enabled);

long get_openssl_verify_result() const;

SSL_CTX *ssl_context() const noexcept;

private: 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;

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;

SSLCTX *ctx; std::mutex ctxmutex; std::vector hostcomponents; std::string ca_cert_filepath; std::string ca_cert_dirpath; bool server_certificateverification = false; long verifyresult = 0; };