From 7a0576546cf8cd336ffdd89774d229c5b3ece53a Mon Sep 17 00:00:00 2001 From: Florian Rupprecht Date: Tue, 23 Nov 2021 16:00:14 +0100 Subject: [PATCH 01/13] GCC 8 workaround --- include/crow/app.h | 4 ++++ include/crow/settings.h | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/include/crow/app.h b/include/crow/app.h index 3d2f22fb8..ef4cec30d 100644 --- a/include/crow/app.h +++ b/include/crow/app.h @@ -82,6 +82,9 @@ namespace crow ///Create a route using a rule (**Use CROW_ROUTE instead**) template +#ifdef CROW_GCC8_WORKAROUND + auto& route(std::string&& rule) +#else auto route(std::string&& rule) #ifdef CROW_CAN_USE_CPP17 -> typename std::invoke_result), @@ -89,6 +92,7 @@ namespace crow #else -> typename std::result_of)( Router, std::string&&)>::type +#endif #endif { return router_.new_rule_tagged(std::move(rule)); diff --git a/include/crow/settings.h b/include/crow/settings.h index bdbd068b0..a38cf7b73 100644 --- a/include/crow/settings.h +++ b/include/crow/settings.h @@ -57,3 +57,7 @@ #define noexcept throw() #endif #endif + +#if __GNUC__ == 8 && __cplusplus > 201103L +#define CROW_GCC8_WORKAROUND +#endif From 58e211355fe9ee2772c94800d981b08596169fdf Mon Sep 17 00:00:00 2001 From: Florian Rupprecht Date: Thu, 25 Nov 2021 15:14:45 +0100 Subject: [PATCH 02/13] Constrain and warn GCC 8 bug --- include/crow/app.h | 2 +- include/crow/settings.h | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/include/crow/app.h b/include/crow/app.h index ef4cec30d..d8845deb2 100644 --- a/include/crow/app.h +++ b/include/crow/app.h @@ -82,7 +82,7 @@ namespace crow ///Create a route using a rule (**Use CROW_ROUTE instead**) template -#ifdef CROW_GCC8_WORKAROUND +#ifdef CROW_GCC83_WORKAROUND auto& route(std::string&& rule) #else auto route(std::string&& rule) diff --git a/include/crow/settings.h b/include/crow/settings.h index a38cf7b73..1437036c7 100644 --- a/include/crow/settings.h +++ b/include/crow/settings.h @@ -58,6 +58,10 @@ #endif #endif -#if __GNUC__ == 8 && __cplusplus > 201103L -#define CROW_GCC8_WORKAROUND +#if __GNUC__ == 8 && __GNUC_MINOR__ < 4 +#if __cplusplus > 201103L +#define CROW_GCC83_WORKAROUND +#else +#warning "GCC 8.1 - 8.3 have a bug that prevents crow from compiling with C++11. Please update GCC to > 8.3 or use C++ > 11." +#endif #endif From df50e3390d9483938bb543edbd10473b74a86e60 Mon Sep 17 00:00:00 2001 From: Florian Rupprecht Date: Thu, 25 Nov 2021 15:16:34 +0100 Subject: [PATCH 03/13] Spelling --- include/crow/settings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/crow/settings.h b/include/crow/settings.h index 1437036c7..4600e2d8e 100644 --- a/include/crow/settings.h +++ b/include/crow/settings.h @@ -62,6 +62,6 @@ #if __cplusplus > 201103L #define CROW_GCC83_WORKAROUND #else -#warning "GCC 8.1 - 8.3 have a bug that prevents crow from compiling with C++11. Please update GCC to > 8.3 or use C++ > 11." +#warning "GCC 8.1 - 8.3 has a bug that prevents crow from compiling with C++11. Please update GCC to > 8.3 or use C++ > 11." #endif #endif From 7ee13c4536a87cda1f35fbf89b238327056c1a05 Mon Sep 17 00:00:00 2001 From: Florian Rupprecht Date: Sat, 27 Nov 2021 14:30:37 +0100 Subject: [PATCH 04/13] GCC 8 workaround compiler detection & error --- include/crow/settings.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/crow/settings.h b/include/crow/settings.h index 4600e2d8e..b2c1c4f50 100644 --- a/include/crow/settings.h +++ b/include/crow/settings.h @@ -58,10 +58,10 @@ #endif #endif -#if __GNUC__ == 8 && __GNUC_MINOR__ < 4 +#if defined(__GNUC__) && __GNUC__ == 8 && __GNUC_MINOR__ < 4 #if __cplusplus > 201103L #define CROW_GCC83_WORKAROUND #else -#warning "GCC 8.1 - 8.3 has a bug that prevents crow from compiling with C++11. Please update GCC to > 8.3 or use C++ > 11." +#error "GCC 8.1 - 8.3 has a bug that prevents crow from compiling with C++11. Please update GCC to > 8.3 or use C++ > 11." #endif #endif From bb48f9529898404667302c8585a4ed8bb20ab311 Mon Sep 17 00:00:00 2001 From: The-EDev Date: Thu, 2 Dec 2021 15:36:52 +0300 Subject: [PATCH 05/13] moved file sending to connection and added check_destroy to it --- include/crow/http_connection.h | 62 +++++++++++++++++++++++++++++-- include/crow/http_response.h | 68 ---------------------------------- 2 files changed, 59 insertions(+), 71 deletions(-) diff --git a/include/crow/http_connection.h b/include/crow/http_connection.h index 9a7fa71ce..cd5bd081c 100644 --- a/include/crow/http_connection.h +++ b/include/crow/http_connection.h @@ -524,7 +524,18 @@ namespace crow { is_writing = true; boost::asio::write(adaptor_.socket(), buffers_); - res.do_stream_file(adaptor_); + + if (res.file_info.statResult == 0) + { + std::ifstream is(res.file_info.path.c_str(), std::ios::in | std::ios::binary); + char buf[16384]; + while (is.read(buf, sizeof(buf)).gcount() > 0) + { + std::vector buffers; + buffers.push_back(boost::asio::buffer(buf)); + do_write_sync(buffers); + } + } res.end(); res.clear(); @@ -550,8 +561,28 @@ namespace crow else { is_writing = true; - boost::asio::write(adaptor_.socket(), buffers_); - res.do_stream_body(adaptor_); + boost::asio::write(adaptor_.socket(), buffers_); // Write the response start / headers + if (res.body.length() > 0) + { + std::string buf; + std::vector buffers; + + while (res.body.length() > 16384) + { + //buf.reserve(16385); + buf = res.body.substr(0, 16384); + res.body = res.body.substr(16384); + buffers.push_back(boost::asio::buffer(buf)); + do_write_sync(buffers); + } + // Collect whatever is left (less than 16KB) and send it down the socket + // buf.reserve(is.length()); + buf = res.body; + res.body.clear(); + + buffers.push_back(boost::asio::buffer(buf)); + do_write_sync(buffers); + } res.end(); res.clear(); @@ -635,6 +666,31 @@ namespace crow }); } + inline void do_write_sync(std::vector& buffers) + { + + boost::asio::write(adaptor_.socket(), buffers, [&](std::error_code ec, std::size_t) { + if (!ec) + { + if (close_connection_) + { + adaptor_.shutdown_write(); + adaptor_.close(); + CROW_LOG_DEBUG << this << " from write (sync)(1)"; + check_destroy(); + } + return false; + } + else + { + CROW_LOG_ERROR << ec << " - happened while sending buffers"; + CROW_LOG_DEBUG << this << " from write (sync)(2)"; + check_destroy(); + return true; + } + }); + } + void check_destroy() { CROW_LOG_DEBUG << this << " is_reading " << is_reading << " is_writing " << is_writing; diff --git a/include/crow/http_response.h b/include/crow/http_response.h index fa3fac1c6..ea6221b4e 100644 --- a/include/crow/http_response.h +++ b/include/crow/http_response.h @@ -53,7 +53,6 @@ namespace crow return crow::get_header_value(headers, key); } - // TODO find a better way to format this so that stuff aren't moved down a line // clang-format off response() {} explicit response(int code) : code(code) {} @@ -242,27 +241,6 @@ namespace crow } } - /// Stream a static file. - template - void do_stream_file(Adaptor& adaptor) - { - if (file_info.statResult == 0) - { - std::ifstream is(file_info.path.c_str(), std::ios::in | std::ios::binary); - write_streamed(is, adaptor); - } - } - - /// Stream the response body (send the body in chunks). - template - void do_stream_body(Adaptor& adaptor) - { - if (body.length() > 0) - { - write_streamed_string(body, adaptor); - } - } - private: bool completed_{}; std::function complete_request_handler_; @@ -280,51 +258,5 @@ namespace crow write_buffer_list(buffers, adaptor); } } - - // THIS METHOD DOES MODIFY THE BODY, AS IN IT EMPTIES IT - template - void write_streamed_string(std::string& is, Adaptor& adaptor) - { - std::string buf; - std::vector buffers; - - while (is.length() > 16384) - { - //buf.reserve(16385); - buf = is.substr(0, 16384); - is = is.substr(16384); - push_and_write(buffers, buf, adaptor); - } - // Collect whatever is left (less than 16KB) and send it down the socket - // buf.reserve(is.length()); - buf = is; - is.clear(); - push_and_write(buffers, buf, adaptor); - } - - template - inline void push_and_write(std::vector& buffers, std::string& buf, Adaptor& adaptor) - { - buffers.clear(); - buffers.push_back(boost::asio::buffer(buf)); - write_buffer_list(buffers, adaptor); - } - - template - inline void write_buffer_list(std::vector& buffers, Adaptor& adaptor) - { - boost::asio::write(adaptor.socket(), buffers, [this](std::error_code ec, std::size_t) { - if (!ec) - { - return false; - } - else - { - CROW_LOG_ERROR << ec << " - happened while sending buffers"; - this->end(); - return true; - } - }); - } }; } // namespace crow From a0a5616d1c73b175a526a3fa2ec0501b4ab02b7d Mon Sep 17 00:00:00 2001 From: The-EDev Date: Thu, 2 Dec 2021 20:18:34 +0300 Subject: [PATCH 06/13] Added missing buffer clears and removed no longer used method from response --- include/crow/http_connection.h | 2 ++ include/crow/http_response.h | 11 ----------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/include/crow/http_connection.h b/include/crow/http_connection.h index cd5bd081c..f3795d1c8 100644 --- a/include/crow/http_connection.h +++ b/include/crow/http_connection.h @@ -572,6 +572,7 @@ namespace crow //buf.reserve(16385); buf = res.body.substr(0, 16384); res.body = res.body.substr(16384); + buffers.clear(); buffers.push_back(boost::asio::buffer(buf)); do_write_sync(buffers); } @@ -580,6 +581,7 @@ namespace crow buf = res.body; res.body.clear(); + buffers.clear(); buffers.push_back(boost::asio::buffer(buf)); do_write_sync(buffers); } diff --git a/include/crow/http_response.h b/include/crow/http_response.h index ea6221b4e..a9fd40c06 100644 --- a/include/crow/http_response.h +++ b/include/crow/http_response.h @@ -247,16 +247,5 @@ namespace crow std::function is_alive_helper_; static_file_info file_info; - template - void write_streamed(Stream& is, Adaptor& adaptor) - { - char buf[16384]; - while (is.read(buf, sizeof(buf)).gcount() > 0) - { - std::vector buffers; - buffers.push_back(boost::asio::buffer(buf)); - write_buffer_list(buffers, adaptor); - } - } }; } // namespace crow From 3c6c675a1867a5e89b1f0ca68df5ab1ad409d5ea Mon Sep 17 00:00:00 2001 From: The-EDev Date: Thu, 2 Dec 2021 20:38:04 +0300 Subject: [PATCH 07/13] formatting --- include/crow/http_response.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/crow/http_response.h b/include/crow/http_response.h index a9fd40c06..db6e4133e 100644 --- a/include/crow/http_response.h +++ b/include/crow/http_response.h @@ -246,6 +246,5 @@ namespace crow std::function complete_request_handler_; std::function is_alive_helper_; static_file_info file_info; - }; } // namespace crow From b309bda1605c35d8886c56ef21ac6a8ad0fc95b1 Mon Sep 17 00:00:00 2001 From: The-EDev Date: Fri, 3 Dec 2021 06:38:14 +0300 Subject: [PATCH 08/13] Updated documentation: added auth guide updated macos setup --- docs/getting_started/setup/macos.md | 18 +++++++ docs/getting_started/setup/windows.md | 2 +- docs/guides/auth.md | 71 +++++++++++++++++++++++++++ mkdocs.yml | 2 + 4 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 docs/guides/auth.md diff --git a/docs/getting_started/setup/macos.md b/docs/getting_started/setup/macos.md index 0955b72ba..ebac4764a 100644 --- a/docs/getting_started/setup/macos.md +++ b/docs/getting_started/setup/macos.md @@ -44,6 +44,10 @@ This will generate a `crow_all.h` file which you can use in the following steps ## Building Crow's tests/examples +!!!note + + This tutorial can be used for Crow projects built with CMake as well + 1. Download and install [Homebrew](https://brew.sh). 2. Run `brew install cmake boost` in your terminal. 3. Get Crow's source code (the entire source code). @@ -55,3 +59,17 @@ This will generate a `crow_all.h` file which you can use in the following steps !!!note You can add options like `-DCROW_ENABLE_SSL`, `-DCROW_ENABLE_COMPRESSION`, or `-DCROW_AMALGAMATE` to `3.c` to build their tests/examples. + +## Compiling using a compiler directly +All you need to do is run the following command: +``` +g++ main.cpp -lpthread +``` +!!!note + + You'll need to install GCC via `brew install gcc`. the Clang compiler should be part of Xcode or Xcode command line tools. + +You can use arguments like `-DCROW_ENABLE_DEBUG`, `-DCROW_ENABLE_COMPRESSION -lz` for HTTP Compression, or `-DCROW_ENABLE_SSL -lssl` for HTTPS support, or even replace g++ with clang++. +!!!warning + + If you're using a version of boost prior to 1.69, you'll need to add the argument `-lboost_system` in order for you Crow application to compile correctly. diff --git a/docs/getting_started/setup/windows.md b/docs/getting_started/setup/windows.md index 7901d358e..92a11b851 100644 --- a/docs/getting_started/setup/windows.md +++ b/docs/getting_started/setup/windows.md @@ -5,7 +5,7 @@ Here's how you can install Crow on your Windows machine. Crow can be simply installed through VCPKG using the command `vcpkg install crow` ### Manually (source or release) -#### Microsoft Visual Studio 2019 and VCPKG +#### Microsoft Visual Studio and VCPKG The following guide will use `example_with_all.cpp` as the Crow application for demonstration purposes. VCPKG will be used only to install Crow's dependencies. 1. Generate `crow_all.h` by navigating to the `scripts` folder and running `python3 merge_all.py ..\include crow_all.h`. diff --git a/docs/guides/auth.md b/docs/guides/auth.md new file mode 100644 index 000000000..ff4988fa6 --- /dev/null +++ b/docs/guides/auth.md @@ -0,0 +1,71 @@ +While Crow doesn't directly support HTTP authentication, it does provide all the tools you need to build your own. This tutorial will show you how to setup basic and token authentication using Crow. + +## Shared information +Every way boils down to the same basic flow: +- The handler calls a verification function. +- The handler provides a `request` and \ a `response`. +- The function returns a `bool` or `enum` status. +- Handler either continues or stops executing based on the returned status. +- Either the function or handler modify and `end()` the `response` in case of failure. + +For the purposes of this tutorial, we will assume that the verification function is defined as `#!cpp bool verify(crow::request req, crow::response res)` + +## Basic Auth +Basic HTTP authentication requires the client to send the Username and Password as a single string, separated by a colon (':') and then encoded as base64. This data needs to be placed in the `Authorization` header of the request. A sample header using the credentials "Username" and "Password" would look like this: `Authorization: Basic VXNlcm5hbWU6UGFzc3dvcmQ=`.

+ +We don't need to worry about creating the request, we only need to extract the credentials from the `Authorization` header and verify them. +!!! note + + There are multiple ways to verify the credentials. Most involve checking the username in a database, then checking a hash of the password against the stored password hash for that username. This tutorial will not go over them + +
+ +To do this we first need to get the `Authorization` header as a string by using the following code: +```cpp +std::string myauth = req.get_header_value("Authorization"); +``` +
+ +Next we need to isolate our encoded credentials and decode them as follows: +```cpp +std::string mycreds = myauth.substr(6); +std::string d_mycreds = crow::utility::base64decode(mycreds, mycreds.size()/*, URLSafe? (true/false)*/); +``` +
+ +Now that we have our `username:password` string, we only need to separate it into 2 different strings and verify their validity: +```cpp +size_t found = d_mycreds.find(':'); +std::string username = d_mycreds.substr(0, found); +std::string password = d_mycreds.substr(found+1); + +/*Verify validity of username and password here*/ +return true; //or false if the username/password are invalid +``` + +## Token Auth +Tokens are some form of unique data that a server can provide to a client in order to verify the client's identity later. While on the surface level they don't provide more security than a strong password, they are designed to be less valuable by being *temporary* and providing *limited access*. Variables like expiration time and access scopes are heavily reliant on the implementation however.

+ +### Access Tokens +The kind of the token itself can vary depending on the implementation and project requirements: Many services use randomly generated strings as tokens. Then compare them against a database to retrieve the associated user data. Some services however prefer using data bearing tokens. One example of the latter kind is JWT, which uses JSON strings encoded in base64 and signed using a private key or an agreed upon secret. While this has the added hassle of signing the token to ensure that it's not been tampered with. It does allow for the client to issue tokens without ever needing to present a password or contact a server. The server would simply be able to verify the signature using the client's public key or secret.

+ +### Using an Access Token +Authenticating with an access token usually involves 2 stages: The first being scquiring the access token from an authority (either by providing credentials such as a username and a password to a server or generating a signed token). The scope of the token (what kind of information it can read or change) is usually defined in this step.

+ +The second stage is simply presenting the Token to the server when requesting a resource. This is even simpler than using basic authentication. All the client needs to do is provide the `Authorization` header with a keyword (usually `Bearer`) followed by the token itself (for example: `Authorization: Bearer ABC123`). Once the client has done that the server will need to acquire this token, which can easily be done as follows:
+ +```cpp +std::string myauth = req.get_header_value("Authorization"); +std::string mycreds = myauth.substr(7); // The length can change based on the keyword used + +/*Verify validity of the token here*/ +return true; //or false if the token is invalid +``` +
+The way of verifying the token is largely up to the implementation, and involves either Bearer token decoding and verification, or database access, neither of which is in this tutorial's scope.

+ +### Refresh Tokens +Some services may choose to provide a refresh token alongside the access token. this token can be used to request a new access token if the existing one has expired. It provides convenience and security in that it makes it possible to acquire new access tokens without the need to expose a password. The downside however is that it can allow a malicious entity to keep its access to a compromised account. As such refresh tokens need to be handled with care, kept secure, and always invalidated as soon as a client logs out or requests a new access token. + +## Sessions +While Crow does not provide built in support for user sessions, a community member was kind enough to provide their own implementation on one of the related issue, their comment along with the code is available [here](https://github.com/CrowCpp/Crow/issues/144#issuecomment-860384771) (Please keep in mind that while we appreciate all efforts to push Crow forward, we cannot provide support for this implementation unless it becomes part of the core project). diff --git a/mkdocs.yml b/mkdocs.yml index d79cc5043..6687405ba 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -53,6 +53,8 @@ nav: - Compression: guides/compression.md - Websockets: guides/websockets.md - Writing Tests: guides/testing.md + - Using Crow: + - HTTP Authorization: guides/auth.md - Server setup: - Proxies: guides/proxies.md - Systemd run on startup: guides/syste.md From 94a2f942bce7eefa40a20fe8c70392ab17103922 Mon Sep 17 00:00:00 2001 From: The-EDev Date: Fri, 3 Dec 2021 06:39:23 +0300 Subject: [PATCH 09/13] changed comment style back to the original (doxygen did not work with the new style) --- include/crow/app.h | 4 ++++ include/crow/http_response.h | 5 +++++ include/crow/json.h | 9 +++++---- include/crow/multipart.h | 1 + include/crow/parser.h | 1 + include/crow/query_string.h | 3 +++ include/crow/routing.h | 6 +++++- include/crow/socket_adaptors.h | 2 +- include/crow/task_timer.h | 18 ++++++------------ include/crow/websocket.h | 25 +++++++++---------------- 10 files changed, 40 insertions(+), 34 deletions(-) diff --git a/include/crow/app.h b/include/crow/app.h index 602fa4748..605a700d5 100644 --- a/include/crow/app.h +++ b/include/crow/app.h @@ -39,6 +39,7 @@ namespace crow using ssl_context_t = boost::asio::ssl::context; #endif /// The main server application + /// /// Use `SimpleApp` or `App` template @@ -57,6 +58,7 @@ namespace crow {} /// Process an Upgrade request + /// /// Currently used to upgrrade an HTTP connection to a WebSocket connection template @@ -157,6 +159,7 @@ namespace crow } /// Set the server's log level + /// /// Possible values are:
/// crow::LogLevel::Debug (0)
@@ -218,6 +221,7 @@ namespace crow } #endif /// A wrapper for `validate()` in the router + /// /// Go through the rules, upgrade them if possible, and add them to the list of rules void validate() diff --git a/include/crow/http_response.h b/include/crow/http_response.h index fa3fac1c6..c9f8a8059 100644 --- a/include/crow/http_response.h +++ b/include/crow/http_response.h @@ -122,6 +122,7 @@ namespace crow } /// Return a "Temporary Redirect" response. + /// /// Location can either be a route or a full URL. void redirect(const std::string& location) @@ -131,6 +132,7 @@ namespace crow } /// Return a "Permanent Redirect" response. + /// /// Location can either be a route or a full URL. void redirect_perm(const std::string& location) @@ -140,6 +142,7 @@ namespace crow } /// Return a "Found (Moved Temporarily)" response. + /// /// Location can either be a route or a full URL. void moved(const std::string& location) @@ -149,6 +152,7 @@ namespace crow } /// Return a "Moved Permanently" response. + /// /// Location can either be a route or a full URL. void moved_perm(const std::string& location) @@ -201,6 +205,7 @@ namespace crow } /// This constains metadata (coming from the `stat` command) related to any static files associated with this response. + /// /// Either a static file or a string body can be returned as 1 response. struct static_file_info diff --git a/include/crow/json.h b/include/crow/json.h index a16194082..8796e9756 100644 --- a/include/crow/json.h +++ b/include/crow/json.h @@ -295,7 +295,7 @@ namespace crow return static_cast(i()); } - ///Return any json value (not object or list) as a string. + /// Return any json value (not object or list) as a string. explicit operator std::string() const { #ifndef CROW_JSON_NO_ERROR_CHECK @@ -405,7 +405,7 @@ namespace crow return detail::r_string{start_, end_}; } - ///The list or object value + /// The list or object value std::vector lo() { #ifndef CROW_JSON_NO_ERROR_CHECK @@ -684,7 +684,7 @@ namespace crow lremain_--; } - /// determines num_type from the string. + /// Determines num_type from the string. void determine_num_type() { if (t_ != type::Number) @@ -1221,8 +1221,9 @@ namespace crow /// JSON write value. + /// - /// Value can mean any json value, including a JSON object. + /// Value can mean any json value, including a JSON object.
/// Write means this class is used to primarily assemble JSON objects using keys and values and export those into a string. class wvalue : public returnable { diff --git a/include/crow/multipart.h b/include/crow/multipart.h index 8bfd6f5b6..5563882db 100644 --- a/include/crow/multipart.h +++ b/include/crow/multipart.h @@ -23,6 +23,7 @@ namespace crow }; ///One part of the multipart message + /// /// It is usually separated from other sections by a `boundary` struct part diff --git a/include/crow/parser.h b/include/crow/parser.h index ad98dbf3d..f8a8c10dd 100644 --- a/include/crow/parser.h +++ b/include/crow/parser.h @@ -11,6 +11,7 @@ namespace crow { /// A wrapper for `nodejs/http-parser`. + /// /// Used to generate a \ref crow.request from the TCP socket buffer. template diff --git a/include/crow/query_string.h b/include/crow/query_string.h index 126aa4ba9..3f13e8a96 100644 --- a/include/crow/query_string.h +++ b/include/crow/query_string.h @@ -363,6 +363,7 @@ namespace crow } /// Get a value from a name, used for `?name=value`. + /// /// Note: this method returns the value of the first occurrence of the key only, to return all occurrences, see \ref get_list(). char* get (const std::string& name) const @@ -391,6 +392,7 @@ namespace crow } /// Returns a list of values, passed as `?name[]=value1&name[]=value2&...name[]=valuen` with n being the size of the list. + /// /// Note: Square brackets in the above example are controlled by `use_brackets` boolean (true by default). If set to false, the example becomes `?name=value1,name=value2...name=valuen` std::vector get_list (const std::string& name, bool use_brackets = true) const @@ -429,6 +431,7 @@ namespace crow } /// Works similar to \ref get_list() except the brackets are mandatory must not be empty. + /// /// For example calling `get_dict(yourname)` on `?yourname[sub1]=42&yourname[sub2]=84` would give a map containing `{sub1 : 42, sub2 : 84}`. /// diff --git a/include/crow/routing.h b/include/crow/routing.h index a33eec784..d95efc33b 100644 --- a/include/crow/routing.h +++ b/include/crow/routing.h @@ -22,6 +22,7 @@ namespace crow constexpr const uint16_t INVALID_BP_ID{0xFFFF}; /// A base class for all rules. + /// /// Used to provide a common interface for code dealing with different types of rules.
/// A Rule provides a URL, allowed HTTP methods, and handlers. @@ -362,6 +363,7 @@ namespace crow /// A rule dealing with websockets. + /// /// Provides the interface for the user to put in the necessary handlers for a websocket to work. class WebSocketRule : public BaseRule @@ -437,6 +439,7 @@ namespace crow }; /// Allows the user to assign parameters using functions. + /// /// `rule.name("name").methods(HTTPMethod::POST)` template @@ -715,7 +718,7 @@ namespace crow Trie() {} - ///Check whether or not the trie is empty. + /// Check whether or not the trie is empty. bool is_empty() { return head_.children.empty(); @@ -1084,6 +1087,7 @@ namespace crow }; /// A blueprint can be considered a smaller section of a Crow app, specifically where the router is conecerned. + /// /// You can use blueprints to assign a common prefix to rules' prefix, set custom static and template folders, and set a custom catchall route. /// You can also assign nest blueprints for maximum Compartmentalization. diff --git a/include/crow/socket_adaptors.h b/include/crow/socket_adaptors.h index 3ce106534..b03368119 100644 --- a/include/crow/socket_adaptors.h +++ b/include/crow/socket_adaptors.h @@ -14,7 +14,7 @@ namespace crow using namespace boost; using tcp = asio::ip::tcp; - ///A wrapper for the asio::ip::tcp::socket and asio::ssl::stream + /// A wrapper for the asio::ip::tcp::socket and asio::ssl::stream struct SocketAdaptor { using context = void; diff --git a/include/crow/task_timer.h b/include/crow/task_timer.h index 1cc838376..707a605c5 100644 --- a/include/crow/task_timer.h +++ b/include/crow/task_timer.h @@ -41,15 +41,13 @@ namespace crow CROW_LOG_DEBUG << "task_timer cancelled: " << this << ' ' << id; } - /// - /// Schedule the given task to be executed after the default amount of - /// ticks. + /// Schedule the given task to be executed after the default amount of ticks. + /// /// \return identifier_type Used to cancel the thread. /// It is not bound to this task_timer instance and in some cases could lead to /// undefined behavior if used with other task_timer objects or after the task /// has been successfully executed. - /// identifier_type schedule(const task_type& task) { tasks_.insert( @@ -60,8 +58,8 @@ namespace crow return highest_id_; } - /// /// Schedule the given task to be executed after the given time. + /// /// \param timeout The amount of ticks (seconds) to wait before execution. /// @@ -69,7 +67,6 @@ namespace crow /// It is not bound to this task_timer instance and in some cases could lead to /// undefined behavior if used with other task_timer objects or after the task /// has been successfully executed. - /// identifier_type schedule(const task_type& task, std::uint8_t timeout) { tasks_.insert({++highest_id_, @@ -78,16 +75,13 @@ namespace crow return highest_id_; } - /// /// Set the default timeout for this task_timer instance. (Default: 5) - /// - /// \param timeout The amount of ticks (seconds) to wait before execution. - /// - void set_default_timeout(std::uint8_t timeout) { default_timeout_ = timeout; } /// + /// \param timeout The amount of ticks (seconds) to wait before execution. + void set_default_timeout(std::uint8_t timeout) { default_timeout_ = timeout; } + /// Get the default timeout. (Default: 5) - /// std::uint8_t get_default_timeout() const { return default_timeout_; } private: diff --git a/include/crow/websocket.h b/include/crow/websocket.h index b5673824c..128909359 100644 --- a/include/crow/websocket.h +++ b/include/crow/websocket.h @@ -64,12 +64,11 @@ namespace crow class Connection : public connection { public: - /// /// Constructor for a connection. + /// /// Requires a request with an "Upgrade: websocket" header.
/// Automatically handles the handshake. - /// Connection(const crow::request& req, Adaptor&& adaptor, std::function open_handler, std::function message_handler, @@ -120,11 +119,10 @@ namespace crow adaptor_.get_io_service().post(handler); } - /// /// Send a "Ping" message. + /// /// Usually invoked to check if the other point is still online. - /// void send_ping(const std::string& msg) override { dispatch([this, msg] { @@ -135,11 +133,10 @@ namespace crow }); } - /// /// Send a "Pong" message. + /// /// Usually automatically invoked as a response to a "Ping" message. - /// void send_pong(const std::string& msg) override { dispatch([this, msg] { @@ -172,11 +169,10 @@ namespace crow }); } - /// /// Send a close signal. + /// /// Sets a flag to destroy the object once the message is sent. - /// void close(const std::string& msg) override { dispatch([this, msg] { @@ -200,6 +196,7 @@ namespace crow } protected: + /// Generate the websocket headers using an opcode and the message size (in bytes). std::string build_header(int opcode, size_t size) { @@ -224,11 +221,10 @@ namespace crow } } - /// /// Send the HTTP upgrade response. + /// /// Finishes the handshake process, then starts reading messages from the socket. - /// void start(std::string&& hello) { static std::string header = "HTTP/1.1 101 Switching Protocols\r\n" @@ -246,14 +242,13 @@ namespace crow do_read(); } - /// /// Read a websocket message. + /// /// Involves:
/// Handling headers (opcodes, size).
/// Unmasking the payload.
/// Reading the actual payload.
- /// void do_read() { is_reading = true; @@ -482,11 +477,10 @@ namespace crow return (mini_header_ & 0x0f00) >> 8; } - /// /// Process the payload fragment. + /// /// Unmasks the fragment, checks the opcode, merges fragments into 1 message body, and calls the appropriate handler. - /// void handle_fragment() { if (has_mask_) @@ -569,11 +563,10 @@ namespace crow fragment_.clear(); } - /// /// Send the buffers' data through the socket. + /// /// Also destroyes the object if the Close flag is set. - /// void do_write() { if (sending_buffers_.empty()) From 78c88bbbb71d8c14264eb884d14a29a90000807d Mon Sep 17 00:00:00 2001 From: The-EDev Date: Fri, 3 Dec 2021 06:41:30 +0300 Subject: [PATCH 10/13] Updated mime_types script: Made script generated code format compliant Added option to have the script download the source file directly Added generation date Also updated the header file to the latest nginx mime.types --- include/crow/mime_types.h | 4 +++- scripts/nginx_mime2cpp.py | 34 ++++++++++++++++++++-------------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/include/crow/mime_types.h b/include/crow/mime_types.h index 38cfefe07..7cf96c20b 100644 --- a/include/crow/mime_types.h +++ b/include/crow/mime_types.h @@ -1,4 +1,4 @@ -// This file is generated from nginx/conf/mime.types using nginx_mime2cpp.py +// This file is generated from nginx/conf/mime.types using nginx_mime2cpp.py on 2021-12-03. #include #include @@ -21,6 +21,7 @@ namespace crow {"jad", "text/vnd.sun.j2me.app-descriptor"}, {"wml", "text/vnd.wap.wml"}, {"htc", "text/x-component"}, + {"avif", "image/avif"}, {"png", "image/png"}, {"svgz", "image/svg+xml"}, {"svg", "image/svg+xml"}, @@ -58,6 +59,7 @@ namespace crow {"xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, {"docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, {"wmlc", "application/vnd.wap.wmlc"}, + {"wasm", "application/wasm"}, {"7z", "application/x-7z-compressed"}, {"cco", "application/x-cocoa"}, {"jardiff", "application/x-java-archive-diff"}, diff --git a/scripts/nginx_mime2cpp.py b/scripts/nginx_mime2cpp.py index ae69c3b1b..1f5bf3d27 100755 --- a/scripts/nginx_mime2cpp.py +++ b/scripts/nginx_mime2cpp.py @@ -3,24 +3,33 @@ #get mime.types file from the nginx repository at nginx/conf/mime.types #typical output filename: mime_types.h import sys +from datetime import date -if len(sys.argv) != 3: - print("Usage: {} ".format(sys.argv[0])) +if (len(sys.argv) != 3) and (len(sys.argv) != 2): + print("Usage (local file): {} ".format(sys.argv[0])) + print("(downloads file) : {} ".format(sys.argv[0])) sys.exit(1) - -file_path = sys.argv[1] -output_path = sys.argv[2] +if len(sys.argv) == 3: + file_path = sys.argv[1] + output_path = sys.argv[2] +elif len(sys.argv) == 2: + import requests + open("mime.types", "wb").write(requests.get("https://hg.nginx.org/nginx/raw-file/tip/conf/mime.types").content) + file_path = "mime.types" + output_path = sys.argv[1] tabspace = " " +tabandahalfspace = " " def main(): outLines = [] - outLines.append("//This file is generated from nginx/conf/mime.types using nginx_mime2cpp.py") + outLines.append("// This file is generated from nginx/conf/mime.types using nginx_mime2cpp.py on " + date.today().strftime('%Y-%m-%d') + ".") outLines.extend([ "#include ", "#include ", "", - "namespace crow {", - tabspace + "const std::unordered_map mime_types {"]) + "namespace crow", + "{", + tabspace + "const std::unordered_map mime_types{"]) with open(file_path, "r") as mtfile: incomplete = "" @@ -39,11 +48,8 @@ def main(): incompleteExists = False outLines.extend(mime_line_to_cpp(splitLine)) - outLines[-1] = outLines[-1][:-1] - outLines.extend([ - tabspace + "};", - "}" - ]) + outLines[-1] = outLines[-1][:-1] + "};" + outLines.append("}") with open(output_path, "w") as mtcppfile: mtcppfile.writelines(x + '\n' for x in outLines) @@ -55,7 +61,7 @@ def mime_line_to_cpp(mlist): if (mlist[i].endswith(";")): mlist[i] = mlist[i][:-1] for i in range (len(mlist)-1, 0, -1): - stringReturn.append(tabspace*2 + "{\"" + mlist[i] + "\", \"" + mlist[0] + "\"},") + stringReturn.append(tabandahalfspace + "{\"" + mlist[i] + "\", \"" + mlist[0] + "\"},") #print("created: " + stringReturn) return stringReturn From 869bc531500e28545e265df498678be4571cf441 Mon Sep 17 00:00:00 2001 From: The-EDev Date: Fri, 3 Dec 2021 07:05:10 +0300 Subject: [PATCH 11/13] format --- include/crow/websocket.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/crow/websocket.h b/include/crow/websocket.h index 128909359..b51d114bf 100644 --- a/include/crow/websocket.h +++ b/include/crow/websocket.h @@ -196,7 +196,6 @@ namespace crow } protected: - /// Generate the websocket headers using an opcode and the message size (in bytes). std::string build_header(int opcode, size_t size) { From b64fc0e34cc155c6411b190f38fd8e04867bebac Mon Sep 17 00:00:00 2001 From: Farook Al-Sammarraie Date: Sat, 4 Dec 2021 04:16:50 +0300 Subject: [PATCH 12/13] typo fix Co-authored-by: Luca Schlecker --- docs/guides/auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/auth.md b/docs/guides/auth.md index ff4988fa6..5cb4e6826 100644 --- a/docs/guides/auth.md +++ b/docs/guides/auth.md @@ -50,7 +50,7 @@ Tokens are some form of unique data that a server can provide to a client in ord The kind of the token itself can vary depending on the implementation and project requirements: Many services use randomly generated strings as tokens. Then compare them against a database to retrieve the associated user data. Some services however prefer using data bearing tokens. One example of the latter kind is JWT, which uses JSON strings encoded in base64 and signed using a private key or an agreed upon secret. While this has the added hassle of signing the token to ensure that it's not been tampered with. It does allow for the client to issue tokens without ever needing to present a password or contact a server. The server would simply be able to verify the signature using the client's public key or secret.

### Using an Access Token -Authenticating with an access token usually involves 2 stages: The first being scquiring the access token from an authority (either by providing credentials such as a username and a password to a server or generating a signed token). The scope of the token (what kind of information it can read or change) is usually defined in this step.

+Authenticating with an access token usually involves 2 stages: The first being acquiring the access token from an authority (either by providing credentials such as a username and a password to a server or generating a signed token). The scope of the token (what kind of information it can read or change) is usually defined in this step.

The second stage is simply presenting the Token to the server when requesting a resource. This is even simpler than using basic authentication. All the client needs to do is provide the `Authorization` header with a keyword (usually `Bearer`) followed by the token itself (for example: `Authorization: Bearer ABC123`). Once the client has done that the server will need to acquire this token, which can easily be done as follows:
From 24d8a72a94942ad49c343c1c584b8f898a078b9e Mon Sep 17 00:00:00 2001 From: Florian Rupprecht <33600480+nx10@users.noreply.github.com> Date: Sat, 4 Dec 2021 12:32:32 +0100 Subject: [PATCH 13/13] Update include/crow/app.h Co-authored-by: Farook Al-Sammarraie --- include/crow/app.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/crow/app.h b/include/crow/app.h index 938b5f7a1..6e7d05f13 100644 --- a/include/crow/app.h +++ b/include/crow/app.h @@ -83,11 +83,11 @@ namespace crow auto& route(std::string&& rule) #else auto route(std::string&& rule) -#ifdef CROW_CAN_USE_CPP17 - -> typename std::invoke_result), Router, std::string&&>::type -#else - -> typename std::result_of)(Router, std::string&&)>::type #endif +#if defined CROW_CAN_USE_CPP17 && !defined CROW_GCC83_WORKAROUND + -> typename std::invoke_result), Router, std::string&&>::type +#elif !defined CROW_GCC83_WORKAROUND + -> typename std::result_of)(Router, std::string&&)>::type #endif { return router_.new_rule_tagged(std::move(rule));