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..5cb4e6826 --- /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 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:
+ +```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/include/crow/app.h b/include/crow/app.h index 602fa4748..264d37b51 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 @@ -77,12 +79,16 @@ namespace crow return router_.new_rule_dynamic(std::move(rule)); } - /// Create a route using a rule (**Use CROW_ROUTE instead**) + ///Create a route using a rule (**Use CROW_ROUTE instead**) template - auto route(std::string&& rule) -#ifdef CROW_CAN_USE_CPP17 - -> typename std::invoke_result), Router, std::string&&>::type +#ifdef CROW_GCC83_WORKAROUND + auto& route(std::string&& rule) #else + auto route(std::string&& rule) +#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 { @@ -157,6 +163,7 @@ namespace crow } /// Set the server's log level + /// /// Possible values are:
/// crow::LogLevel::Debug (0)
@@ -218,6 +225,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_connection.h b/include/crow/http_connection.h index 4af28f6d0..d370d3fa0 100644 --- a/include/crow/http_connection.h +++ b/include/crow/http_connection.h @@ -526,7 +526,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(); @@ -552,8 +563,30 @@ 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.clear(); + 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.clear(); + buffers.push_back(boost::asio::buffer(buf)); + do_write_sync(buffers); + } res.end(); res.clear(); @@ -637,6 +670,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..3ff27ff22 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) {} @@ -122,6 +121,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 +131,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 +141,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 +151,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 +204,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 @@ -242,89 +246,10 @@ 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_; 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); - } - } - - // 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 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/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/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/settings.h b/include/crow/settings.h index bdbd068b0..b2c1c4f50 100644 --- a/include/crow/settings.h +++ b/include/crow/settings.h @@ -57,3 +57,11 @@ #define noexcept throw() #endif #endif + +#if defined(__GNUC__) && __GNUC__ == 8 && __GNUC_MINOR__ < 4 +#if __cplusplus > 201103L +#define CROW_GCC83_WORKAROUND +#else +#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 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..b51d114bf 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] { @@ -224,11 +220,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 +241,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 +476,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 +562,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()) 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 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