From ed46b43fa7ff56a605b35a50bb08649dea372928 Mon Sep 17 00:00:00 2001 From: eidheim Date: Fri, 26 Jul 2019 09:28:20 +0200 Subject: [PATCH] Improved comments on public functions and variables as suggested in https://github.com/openjournals/joss-reviews/issues/1592#issuecomment-514946444 --- client_http.hpp | 31 +++++++++++++---------- client_https.hpp | 15 +++++++++--- crypto.hpp | 32 ++++++++++++++++++++---- mutex.hpp | 3 ++- server_http.hpp | 31 ++++++++++++++--------- server_https.hpp | 11 +++++++-- utility.hpp | 64 +++++++++++++++++++++++++++++++----------------- 7 files changed, 130 insertions(+), 57 deletions(-) diff --git a/client_http.hpp b/client_http.hpp index 282d452..11ad753 100644 --- a/client_http.hpp +++ b/client_http.hpp @@ -15,6 +15,7 @@ namespace SimpleWeb { int lflf = 0; public: + /// Match condition for asio::read_until to match both standard and non-standard HTTP header endings. std::pair, bool> operator()(asio::buffers_iterator begin, asio::buffers_iterator end) { auto it = begin; for(; it != end; ++it) { @@ -72,7 +73,7 @@ namespace SimpleWeb { std::size_t size() noexcept { return streambuf.size(); } - /// Convenience function to return std::string. The stream buffer is consumed. + /// Convenience function to return content as a string. The stream buffer is consumed. std::string string() noexcept { try { std::string str; @@ -188,15 +189,14 @@ namespace SimpleWeb { }; public: - /// Set before calling request + /// Set before calling a request function. Config config; - /// If you have your own asio::io_service, store its pointer here before calling request(). - /// When using asynchronous requests, running the io_service is up to the programmer. + /// If you want to reuse an already created asio::io_service, store its pointer here before calling a request function. std::shared_ptr io_service; /// Convenience function to perform synchronous request. The io_service is run within this function. - /// If reusing the io_service for other tasks, use the asynchronous request functions instead. + /// If you reuse the io_service for other tasks, use the asynchronous request functions instead. /// Do not use concurrently with the asynchronous request functions. /// When requesting Server-Sent Events: will throw on error::eof, please use asynchronous request functions instead. std::shared_ptr request(const std::string &method, const std::string &path = {"/"}, string_view content = {}, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) { @@ -226,7 +226,7 @@ namespace SimpleWeb { } /// Convenience function to perform synchronous request. The io_service is run within this function. - /// If reusing the io_service for other tasks, use the asynchronous request functions instead. + /// If you reuse the io_service for other tasks, use the asynchronous request functions instead. /// Do not use concurrently with the asynchronous request functions. /// When requesting Server-Sent Events: will throw on error::eof, please use asynchronous request functions instead. std::shared_ptr request(const std::string &method, const std::string &path, std::istream &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) { @@ -255,7 +255,7 @@ namespace SimpleWeb { return response; } - /// Asynchronous request where setting and/or running Client's io_service is required. + /// Asynchronous request where running Client's io_service is required. /// Do not use concurrently with the synchronous request functions. /// When requesting Server-Sent Events: request_callback might be called more than twice, first call with empty contents on open, and with ec = error::eof on last call void request(const std::string &method, const std::string &path, string_view content, const CaseInsensitiveMultimap &header, @@ -307,7 +307,7 @@ namespace SimpleWeb { connect(session); } - /// Asynchronous request where setting and/or running Client's io_service is required. + /// Asynchronous request where running Client's io_service is required. /// Do not use concurrently with the synchronous request functions. /// When requesting Server-Sent Events: request_callback might be called more than twice, first call with empty contents on open, and with ec = error::eof on last call void request(const std::string &method, const std::string &path, string_view content, @@ -315,7 +315,7 @@ namespace SimpleWeb { request(method, path, content, CaseInsensitiveMultimap(), std::move(request_callback_)); } - /// Asynchronous request where setting and/or running Client's io_service is required. + /// Asynchronous request where running Client's io_service is required. /// Do not use concurrently with the synchronous request functions. /// When requesting Server-Sent Events: request_callback might be called more than twice, first call with empty contents on open, and with ec = error::eof on last call void request(const std::string &method, const std::string &path, @@ -323,14 +323,14 @@ namespace SimpleWeb { request(method, path, std::string(), CaseInsensitiveMultimap(), std::move(request_callback_)); } - /// Asynchronous request where setting and/or running Client's io_service is required. + /// Asynchronous request where running Client's io_service is required. /// Do not use concurrently with the synchronous request functions. /// When requesting Server-Sent Events: request_callback might be called more than twice, first call with empty contents on open, and with ec = error::eof on last call void request(const std::string &method, std::function, const error_code &)> &&request_callback_) { request(method, std::string("/"), std::string(), CaseInsensitiveMultimap(), std::move(request_callback_)); } - /// Asynchronous request where setting and/or running Client's io_service is required. + /// Asynchronous request where running Client's io_service is required. /// Do not use concurrently with the synchronous request functions. /// When requesting Server-Sent Events: request_callback might be called more than twice, first call with empty contents on open, and with ec = error::eof on last call void request(const std::string &method, const std::string &path, std::istream &content, const CaseInsensitiveMultimap &header, @@ -386,7 +386,7 @@ namespace SimpleWeb { connect(session); } - /// Asynchronous request where setting and/or running Client's io_service is required. + /// Asynchronous request where running Client's io_service is required. /// Do not use concurrently with the synchronous request functions. /// When requesting Server-Sent Events: request_callback might be called more than twice, first call with empty contents on open, and with ec = error::eof on last call void request(const std::string &method, const std::string &path, std::istream &content, @@ -394,7 +394,7 @@ namespace SimpleWeb { request(method, path, content, CaseInsensitiveMultimap(), std::move(request_callback_)); } - /// Close connections + /// Close connections. void stop() noexcept { LockGuard lock(connections_mutex); for(auto it = connections.begin(); it != connections.end();) { @@ -755,6 +755,11 @@ namespace SimpleWeb { template <> class Client : public ClientBase { public: + /** + * Constructs a client object. + * + * @param server_port_path Server resource given by host[:port][/path] + */ Client(const std::string &server_port_path) noexcept : ClientBase::ClientBase(server_port_path, 80) {} protected: diff --git a/client_https.hpp b/client_https.hpp index 864cc61..8bc2f0f 100644 --- a/client_https.hpp +++ b/client_https.hpp @@ -15,11 +15,20 @@ namespace SimpleWeb { template <> class Client : public ClientBase { public: - Client(const std::string &server_port_path, bool verify_certificate = true, const std::string &cert_file = std::string(), + /** + * Constructs a client object. + * + * @param server_port_path Server resource given by host[:port][/path] + * @param verify_certificate Set to true (default) to verify the server's certificate and hostname according to RFC 2818. + * @param certification_file If non-empty, sends the given certification file to server. Requires private_key_file. + * @param private_key_file If non-empty, specifies the file containing the private key for certification_file. Requires certification_file. + * @param verify_file If non-empty, use this certificate authority file to perform verification. + */ + Client(const std::string &server_port_path, bool verify_certificate = true, const std::string &certification_file = std::string(), const std::string &private_key_file = std::string(), const std::string &verify_file = std::string()) : ClientBase::ClientBase(server_port_path, 443), context(asio::ssl::context::tlsv12) { - if(cert_file.size() > 0 && private_key_file.size() > 0) { - context.use_certificate_chain_file(cert_file); + if(certification_file.size() > 0 && private_key_file.size() > 0) { + context.use_certificate_chain_file(certification_file); context.use_private_key_file(private_key_file, asio::ssl::context::pem); } diff --git a/crypto.hpp b/crypto.hpp index 981f214..62b4237 100644 --- a/crypto.hpp +++ b/crypto.hpp @@ -27,7 +27,8 @@ namespace SimpleWeb { public: class Base64 { public: - static std::string encode(const std::string &ascii) noexcept { + /// Returns Base64 encoded string from input string. + static std::string encode(const std::string &input) noexcept { std::string base64; BIO *bio, *b64; @@ -40,13 +41,13 @@ namespace SimpleWeb { BIO_set_mem_buf(b64, bptr, BIO_CLOSE); // Write directly to base64-buffer to avoid copy - auto base64_length = static_cast(round(4 * ceil(static_cast(ascii.size()) / 3.0))); + auto base64_length = static_cast(round(4 * ceil(static_cast(input.size()) / 3.0))); base64.resize(base64_length); bptr->length = 0; bptr->max = base64_length + 1; bptr->data = &base64[0]; - if(BIO_write(b64, &ascii[0], static_cast(ascii.size())) <= 0 || BIO_flush(b64) <= 0) + if(BIO_write(b64, &input[0], static_cast(input.size())) <= 0 || BIO_flush(b64) <= 0) base64.clear(); // To keep &base64[0] through BIO_free_all(b64) @@ -59,6 +60,7 @@ namespace SimpleWeb { return base64; } + /// Returns Base64 decoded string from base64 input. static std::string decode(const std::string &base64) noexcept { std::string ascii; @@ -88,7 +90,7 @@ namespace SimpleWeb { } }; - /// Return hex string from bytes in input string. + /// Returns hex string from bytes in input string. static std::string to_hex_string(const std::string &input) noexcept { std::stringstream hex_stream; hex_stream << std::hex << std::internal << std::setfill('0'); @@ -97,6 +99,7 @@ namespace SimpleWeb { return hex_stream.str(); } + /// Returns md5 hash value from input string. static std::string md5(const std::string &input, std::size_t iterations = 1) noexcept { std::string hash; @@ -109,6 +112,7 @@ namespace SimpleWeb { return hash; } + /// Returns md5 hash value from input stream. static std::string md5(std::istream &stream, std::size_t iterations = 1) noexcept { MD5_CTX context; MD5_Init(&context); @@ -126,6 +130,7 @@ namespace SimpleWeb { return hash; } + /// Returns sha1 hash value from input string. static std::string sha1(const std::string &input, std::size_t iterations = 1) noexcept { std::string hash; @@ -138,6 +143,7 @@ namespace SimpleWeb { return hash; } + /// Returns sha1 hash value from input stream. static std::string sha1(std::istream &stream, std::size_t iterations = 1) noexcept { SHA_CTX context; SHA1_Init(&context); @@ -155,6 +161,7 @@ namespace SimpleWeb { return hash; } + /// Returns sha256 hash value from input string. static std::string sha256(const std::string &input, std::size_t iterations = 1) noexcept { std::string hash; @@ -167,6 +174,7 @@ namespace SimpleWeb { return hash; } + /// Returns sha256 hash value from input stream. static std::string sha256(std::istream &stream, std::size_t iterations = 1) noexcept { SHA256_CTX context; SHA256_Init(&context); @@ -184,6 +192,7 @@ namespace SimpleWeb { return hash; } + /// Returns sha512 hash value from input string. static std::string sha512(const std::string &input, std::size_t iterations = 1) noexcept { std::string hash; @@ -196,6 +205,7 @@ namespace SimpleWeb { return hash; } + /// Returns sha512 hash value from input stream. static std::string sha512(std::istream &stream, std::size_t iterations = 1) noexcept { SHA512_CTX context; SHA512_Init(&context); @@ -213,7 +223,19 @@ namespace SimpleWeb { return hash; } - /// key_size is number of bytes of the returned key. + /// Returns PBKDF2 hash value from the given password + /// Input parameter key_size number of bytes of the returned key. + + /** + * Returns PBKDF2 derived key from the given password. + * + * @param password The password to derive key from. + * @param salt The salt to be used in the algorithm. + * @param iterations Number of iterations to be used in the algorithm. + * @param key_size Number of bytes of the returned key. + * + * @return The PBKDF2 derived key. + */ static std::string pbkdf2(const std::string &password, const std::string &salt, int iterations, int key_size) noexcept { std::string key; key.resize(static_cast(key_size)); diff --git a/mutex.hpp b/mutex.hpp index 755c20c..2711850 100644 --- a/mutex.hpp +++ b/mutex.hpp @@ -69,7 +69,7 @@ THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) namespace SimpleWeb { - /// Defines an annotated interface for mutexes. + /// Mutex class that is annotated for Clang Thread Safety Analysis. class CAPABILITY("mutex") Mutex { std::mutex mutex; @@ -83,6 +83,7 @@ namespace SimpleWeb { } }; + /// Scoped mutex guard class that is annotated for Clang Thread Safety Analysis. class SCOPED_CAPABILITY LockGuard { Mutex &mutex; bool locked = true; diff --git a/server_http.hpp b/server_http.hpp index f671b54..ac45288 100644 --- a/server_http.hpp +++ b/server_http.hpp @@ -36,6 +36,7 @@ namespace SimpleWeb { class Session; public: + /// Response class where the content of the response is sent to client when the object is about to be destroyed. class Response : public std::enable_shared_from_this, public std::ostream { friend class ServerBase; friend class Server; @@ -124,7 +125,9 @@ namespace SimpleWeb { return streambuf->size(); } - /// Use this function if you need to recursively send parts of a longer message, or when using server-sent events (SSE). + /// Send the content of the response stream to client. The callback is called when the send has completed. + /// + /// Use this function if you need to recursively send parts of a longer message, or when using server-sent events. void send(const std::function &callback = nullptr) noexcept { session->connection->set_timeout(timeout_content); @@ -138,18 +141,18 @@ namespace SimpleWeb { send_from_queue(); } - /// Write directly to stream buffer using std::ostream::write + /// Write directly to stream buffer using std::ostream::write. void write(const char_type *ptr, std::streamsize n) { std::ostream::write(ptr, n); } - /// Convenience function for writing status line, potential header fields, and empty content + /// Convenience function for writing status line, potential header fields, and empty content. void write(StatusCode status_code = StatusCode::success_ok, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) { *this << "HTTP/1.1 " << SimpleWeb::status_code(status_code) << "\r\n"; write_header(header, 0); } - /// Convenience function for writing status line, header fields, and content + /// Convenience function for writing status line, header fields, and content. void write(StatusCode status_code, string_view content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) { *this << "HTTP/1.1 " << SimpleWeb::status_code(status_code) << "\r\n"; write_header(header, content.size()); @@ -157,7 +160,7 @@ namespace SimpleWeb { *this << content; } - /// Convenience function for writing status line, header fields, and content + /// Convenience function for writing status line, header fields, and content. void write(StatusCode status_code, std::istream &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) { *this << "HTTP/1.1 " << SimpleWeb::status_code(status_code) << "\r\n"; content.seekg(0, std::ios::end); @@ -168,22 +171,22 @@ namespace SimpleWeb { *this << content.rdbuf(); } - /// Convenience function for writing success status line, header fields, and content + /// Convenience function for writing success status line, header fields, and content. void write(string_view content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) { write(StatusCode::success_ok, content, header); } - /// Convenience function for writing success status line, header fields, and content + /// Convenience function for writing success status line, header fields, and content. void write(std::istream &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) { write(StatusCode::success_ok, content, header); } - /// Convenience function for writing success status line, and header fields + /// Convenience function for writing success status line, and header fields. void write(const CaseInsensitiveMultimap &header) { write(StatusCode::success_ok, std::string(), header); } - /// If true, force server to close the connection after the response have been sent. + /// If set to true, force server to close the connection after the response have been sent. /// /// This is useful when implementing a HTTP/1.0-server sending content /// without specifying the content length. @@ -197,7 +200,7 @@ namespace SimpleWeb { std::size_t size() noexcept { return streambuf.size(); } - /// Convenience function to return std::string. The stream buffer is consumed. + /// Convenience function to return content as std::string. The stream buffer is consumed. std::string string() noexcept { try { std::string str; @@ -233,6 +236,7 @@ namespace SimpleWeb { CaseInsensitiveMultimap header; + /// The result of the resource regular expression match of the request path. regex::smatch path_match; std::shared_ptr remote_endpoint; @@ -363,16 +367,20 @@ namespace SimpleWeb { }; public: + /// Use this container to add resources for specific request paths depending on the given regex and method. /// Warning: do not add or remove resources after start() is called std::map::Response>, std::shared_ptr::Request>)>>> resource; + /// If the request path does not match a resource regex, this function is called. std::map::Response>, std::shared_ptr::Request>)>> default_resource; + /// Called when an error occurs. std::function::Request>, const error_code &)> on_error; + /// Called on upgrade requests. std::function &, std::shared_ptr::Request>)> on_upgrade; - /// If you have your own asio::io_service, store its pointer here before running start(). + /// If you want to reuse an already created asio::io_service, store its pointer here before calling start(). std::shared_ptr io_service; /// If you know the server port in advance, use start() instead. @@ -762,6 +770,7 @@ namespace SimpleWeb { template <> class Server : public ServerBase { public: + /// Constructs a server object. Server() noexcept : ServerBase::ServerBase(80) {} protected: diff --git a/server_https.hpp b/server_https.hpp index 477b333..f6e3b47 100644 --- a/server_https.hpp +++ b/server_https.hpp @@ -20,9 +20,16 @@ namespace SimpleWeb { bool set_session_id_context = false; public: - Server(const std::string &cert_file, const std::string &private_key_file, const std::string &verify_file = std::string()) + /** + * Constructs a server object. + * + * @param certification_file If non-empty, sends the given certification file to client. + * @param private_key_file Specifies the file containing the private key for certification_file. + * @param verify_file If non-empty, use this certificate authority file to perform verification of client's certificate and hostname according to RFC 2818. + */ + Server(const std::string &certification_file, const std::string &private_key_file, const std::string &verify_file = std::string()) : ServerBase::ServerBase(443), context(asio::ssl::context::tlsv12) { - context.use_certificate_chain_file(cert_file); + context.use_certificate_chain_file(certification_file); context.use_private_key_file(private_key_file, asio::ssl::context::pem); if(verify_file.size() > 0) { diff --git a/utility.hpp b/utility.hpp index 9880822..5b3318d 100644 --- a/utility.hpp +++ b/utility.hpp @@ -151,7 +151,7 @@ namespace SimpleWeb { class HttpHeader { public: - /// Parse header fields + /// Parse header fields from stream static CaseInsensitiveMultimap parse(std::istream &stream) noexcept { CaseInsensitiveMultimap result; std::string line; @@ -170,36 +170,37 @@ namespace SimpleWeb { public: class SemicolonSeparatedAttributes { public: - /// Parse Set-Cookie or Content-Disposition header field value. Attribute values are percent-decoded. - static CaseInsensitiveMultimap parse(const std::string &str) { + /// Parse Set-Cookie or Content-Disposition from given header field value. + /// Attribute values are percent-decoded. + static CaseInsensitiveMultimap parse(const std::string &value) { CaseInsensitiveMultimap result; std::size_t name_start_pos = std::string::npos; std::size_t name_end_pos = std::string::npos; std::size_t value_start_pos = std::string::npos; - for(std::size_t c = 0; c < str.size(); ++c) { + for(std::size_t c = 0; c < value.size(); ++c) { if(name_start_pos == std::string::npos) { - if(str[c] != ' ' && str[c] != ';') + if(value[c] != ' ' && value[c] != ';') name_start_pos = c; } else { if(name_end_pos == std::string::npos) { - if(str[c] == ';') { - result.emplace(str.substr(name_start_pos, c - name_start_pos), std::string()); + if(value[c] == ';') { + result.emplace(value.substr(name_start_pos, c - name_start_pos), std::string()); name_start_pos = std::string::npos; } - else if(str[c] == '=') + else if(value[c] == '=') name_end_pos = c; } else { if(value_start_pos == std::string::npos) { - if(str[c] == '"' && c + 1 < str.size()) + if(value[c] == '"' && c + 1 < value.size()) value_start_pos = c + 1; else value_start_pos = c; } - else if(str[c] == '"' || str[c] == ';') { - result.emplace(str.substr(name_start_pos, name_end_pos - name_start_pos), Percent::decode(str.substr(value_start_pos, c - value_start_pos))); + else if(value[c] == '"' || value[c] == ';') { + result.emplace(value.substr(name_start_pos, name_end_pos - name_start_pos), Percent::decode(value.substr(value_start_pos, c - value_start_pos))); name_start_pos = std::string::npos; name_end_pos = std::string::npos; value_start_pos = std::string::npos; @@ -209,12 +210,12 @@ namespace SimpleWeb { } if(name_start_pos != std::string::npos) { if(name_end_pos == std::string::npos) - result.emplace(str.substr(name_start_pos), std::string()); + result.emplace(value.substr(name_start_pos), std::string()); else if(value_start_pos != std::string::npos) { - if(str.back() == '"') - result.emplace(str.substr(name_start_pos, name_end_pos - name_start_pos), Percent::decode(str.substr(value_start_pos, str.size() - 1))); + if(value.back() == '"') + result.emplace(value.substr(name_start_pos, name_end_pos - name_start_pos), Percent::decode(value.substr(value_start_pos, value.size() - 1))); else - result.emplace(str.substr(name_start_pos, name_end_pos - name_start_pos), Percent::decode(str.substr(value_start_pos))); + result.emplace(value.substr(name_start_pos, name_end_pos - name_start_pos), Percent::decode(value.substr(value_start_pos))); } } @@ -222,11 +223,21 @@ namespace SimpleWeb { } }; }; - }; // namespace SimpleWeb + }; class RequestMessage { public: - /// Parse request line and header fields + /** Parse request line and header fields from a request stream. + * + * @param[in] stream Stream to parse. + * @param[out] method HTTP method. + * @param[out] path Path from request URI. + * @param[out] query_string Query string from request URI. + * @param[out] version HTTP version. + * @param[out] header Header fields. + * + * @return True if stream is parsed successfully, false if not. + */ static bool parse(std::istream &stream, std::string &method, std::string &path, std::string &query_string, std::string &version, CaseInsensitiveMultimap &header) noexcept { std::string line; std::size_t method_end; @@ -273,7 +284,15 @@ namespace SimpleWeb { class ResponseMessage { public: - /// Parse status line and header fields + /** Parse status line and header fields from a response stream. + * + * @param[in] stream Stream to parse. + * @param[out] version HTTP version. + * @param[out] status_code HTTP status code. + * @param[out] header Header fields. + * + * @return True if stream is parsed successfully, false if not. + */ static bool parse(std::istream &stream, std::string &version, std::string &status_code, CaseInsensitiveMultimap &header) noexcept { std::string line; std::size_t version_end; @@ -314,9 +333,9 @@ namespace SimpleWeb { #endif namespace SimpleWeb { - /// Makes it possible to for instance cancel Asio handlers without stopping asio::io_service + /// Makes it possible to for instance cancel Asio handlers without stopping asio::io_service. class ScopeRunner { - /// Scope count that is set to -1 if scopes are to be canceled + /// Scope count that is set to -1 if scopes are to be canceled. std::atomic count; public: @@ -335,7 +354,8 @@ namespace SimpleWeb { ScopeRunner() noexcept : count(0) {} - /// Returns nullptr if scope should be exited, or a shared lock otherwise + /// Returns nullptr if scope should be exited, or a shared lock otherwise. + /// The shared lock ensures that a potential destructor call is delayed until all locks are released. std::unique_ptr continue_lock() noexcept { long expected = count; while(expected >= 0 && !count.compare_exchange_weak(expected, expected + 1)) @@ -347,7 +367,7 @@ namespace SimpleWeb { return std::unique_ptr(new SharedLock(count)); } - /// Blocks until all shared locks are released, then prevents future shared locks + /// Blocks until all shared locks are released, then prevents future shared locks. void stop() noexcept { long expected = 0; while(!count.compare_exchange_weak(expected, -1)) {