Improved comments on public functions and variables as suggested in https://github.com/openjournals/joss-reviews/issues/1592#issuecomment-514946444

This commit is contained in:
eidheim 2019-07-26 09:28:20 +02:00
commit ed46b43fa7
7 changed files with 130 additions and 57 deletions

View file

@ -15,6 +15,7 @@ namespace SimpleWeb {
int lflf = 0; int lflf = 0;
public: public:
/// Match condition for asio::read_until to match both standard and non-standard HTTP header endings.
std::pair<asio::buffers_iterator<asio::const_buffers_1>, bool> operator()(asio::buffers_iterator<asio::const_buffers_1> begin, asio::buffers_iterator<asio::const_buffers_1> end) { std::pair<asio::buffers_iterator<asio::const_buffers_1>, bool> operator()(asio::buffers_iterator<asio::const_buffers_1> begin, asio::buffers_iterator<asio::const_buffers_1> end) {
auto it = begin; auto it = begin;
for(; it != end; ++it) { for(; it != end; ++it) {
@ -72,7 +73,7 @@ namespace SimpleWeb {
std::size_t size() noexcept { std::size_t size() noexcept {
return streambuf.size(); 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 { std::string string() noexcept {
try { try {
std::string str; std::string str;
@ -188,15 +189,14 @@ namespace SimpleWeb {
}; };
public: public:
/// Set before calling request /// Set before calling a request function.
Config config; Config config;
/// If you have your own asio::io_service, store its pointer here before calling request(). /// If you want to reuse an already created asio::io_service, store its pointer here before calling a request function.
/// When using asynchronous requests, running the io_service is up to the programmer.
std::shared_ptr<io_context> io_service; std::shared_ptr<io_context> io_service;
/// Convenience function to perform synchronous request. The io_service is run within this function. /// 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. /// 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. /// When requesting Server-Sent Events: will throw on error::eof, please use asynchronous request functions instead.
std::shared_ptr<Response> request(const std::string &method, const std::string &path = {"/"}, string_view content = {}, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) { std::shared_ptr<Response> 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. /// 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. /// 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. /// When requesting Server-Sent Events: will throw on error::eof, please use asynchronous request functions instead.
std::shared_ptr<Response> request(const std::string &method, const std::string &path, std::istream &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) { std::shared_ptr<Response> request(const std::string &method, const std::string &path, std::istream &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
@ -255,7 +255,7 @@ namespace SimpleWeb {
return response; 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. /// 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 /// 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, void request(const std::string &method, const std::string &path, string_view content, const CaseInsensitiveMultimap &header,
@ -307,7 +307,7 @@ namespace SimpleWeb {
connect(session); 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. /// 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 /// 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, 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_)); 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. /// 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 /// 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, 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_)); 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. /// 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 /// 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<void(std::shared_ptr<Response>, const error_code &)> &&request_callback_) { void request(const std::string &method, std::function<void(std::shared_ptr<Response>, const error_code &)> &&request_callback_) {
request(method, std::string("/"), std::string(), CaseInsensitiveMultimap(), std::move(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. /// 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 /// 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, void request(const std::string &method, const std::string &path, std::istream &content, const CaseInsensitiveMultimap &header,
@ -386,7 +386,7 @@ namespace SimpleWeb {
connect(session); 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. /// 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 /// 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, 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_)); request(method, path, content, CaseInsensitiveMultimap(), std::move(request_callback_));
} }
/// Close connections /// Close connections.
void stop() noexcept { void stop() noexcept {
LockGuard lock(connections_mutex); LockGuard lock(connections_mutex);
for(auto it = connections.begin(); it != connections.end();) { for(auto it = connections.begin(); it != connections.end();) {
@ -755,6 +755,11 @@ namespace SimpleWeb {
template <> template <>
class Client<HTTP> : public ClientBase<HTTP> { class Client<HTTP> : public ClientBase<HTTP> {
public: 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<HTTP>::ClientBase(server_port_path, 80) {} Client(const std::string &server_port_path) noexcept : ClientBase<HTTP>::ClientBase(server_port_path, 80) {}
protected: protected:

View file

@ -15,11 +15,20 @@ namespace SimpleWeb {
template <> template <>
class Client<HTTPS> : public ClientBase<HTTPS> { class Client<HTTPS> : public ClientBase<HTTPS> {
public: 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()) const std::string &private_key_file = std::string(), const std::string &verify_file = std::string())
: ClientBase<HTTPS>::ClientBase(server_port_path, 443), context(asio::ssl::context::tlsv12) { : ClientBase<HTTPS>::ClientBase(server_port_path, 443), context(asio::ssl::context::tlsv12) {
if(cert_file.size() > 0 && private_key_file.size() > 0) { if(certification_file.size() > 0 && private_key_file.size() > 0) {
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); context.use_private_key_file(private_key_file, asio::ssl::context::pem);
} }

View file

@ -27,7 +27,8 @@ namespace SimpleWeb {
public: public:
class Base64 { class Base64 {
public: 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; std::string base64;
BIO *bio, *b64; BIO *bio, *b64;
@ -40,13 +41,13 @@ namespace SimpleWeb {
BIO_set_mem_buf(b64, bptr, BIO_CLOSE); BIO_set_mem_buf(b64, bptr, BIO_CLOSE);
// Write directly to base64-buffer to avoid copy // Write directly to base64-buffer to avoid copy
auto base64_length = static_cast<std::size_t>(round(4 * ceil(static_cast<double>(ascii.size()) / 3.0))); auto base64_length = static_cast<std::size_t>(round(4 * ceil(static_cast<double>(input.size()) / 3.0)));
base64.resize(base64_length); base64.resize(base64_length);
bptr->length = 0; bptr->length = 0;
bptr->max = base64_length + 1; bptr->max = base64_length + 1;
bptr->data = &base64[0]; bptr->data = &base64[0];
if(BIO_write(b64, &ascii[0], static_cast<int>(ascii.size())) <= 0 || BIO_flush(b64) <= 0) if(BIO_write(b64, &input[0], static_cast<int>(input.size())) <= 0 || BIO_flush(b64) <= 0)
base64.clear(); base64.clear();
// To keep &base64[0] through BIO_free_all(b64) // To keep &base64[0] through BIO_free_all(b64)
@ -59,6 +60,7 @@ namespace SimpleWeb {
return base64; return base64;
} }
/// Returns Base64 decoded string from base64 input.
static std::string decode(const std::string &base64) noexcept { static std::string decode(const std::string &base64) noexcept {
std::string ascii; 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 { static std::string to_hex_string(const std::string &input) noexcept {
std::stringstream hex_stream; std::stringstream hex_stream;
hex_stream << std::hex << std::internal << std::setfill('0'); hex_stream << std::hex << std::internal << std::setfill('0');
@ -97,6 +99,7 @@ namespace SimpleWeb {
return hex_stream.str(); 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 { static std::string md5(const std::string &input, std::size_t iterations = 1) noexcept {
std::string hash; std::string hash;
@ -109,6 +112,7 @@ namespace SimpleWeb {
return hash; return hash;
} }
/// Returns md5 hash value from input stream.
static std::string md5(std::istream &stream, std::size_t iterations = 1) noexcept { static std::string md5(std::istream &stream, std::size_t iterations = 1) noexcept {
MD5_CTX context; MD5_CTX context;
MD5_Init(&context); MD5_Init(&context);
@ -126,6 +130,7 @@ namespace SimpleWeb {
return hash; return hash;
} }
/// Returns sha1 hash value from input string.
static std::string sha1(const std::string &input, std::size_t iterations = 1) noexcept { static std::string sha1(const std::string &input, std::size_t iterations = 1) noexcept {
std::string hash; std::string hash;
@ -138,6 +143,7 @@ namespace SimpleWeb {
return hash; return hash;
} }
/// Returns sha1 hash value from input stream.
static std::string sha1(std::istream &stream, std::size_t iterations = 1) noexcept { static std::string sha1(std::istream &stream, std::size_t iterations = 1) noexcept {
SHA_CTX context; SHA_CTX context;
SHA1_Init(&context); SHA1_Init(&context);
@ -155,6 +161,7 @@ namespace SimpleWeb {
return hash; return hash;
} }
/// Returns sha256 hash value from input string.
static std::string sha256(const std::string &input, std::size_t iterations = 1) noexcept { static std::string sha256(const std::string &input, std::size_t iterations = 1) noexcept {
std::string hash; std::string hash;
@ -167,6 +174,7 @@ namespace SimpleWeb {
return hash; return hash;
} }
/// Returns sha256 hash value from input stream.
static std::string sha256(std::istream &stream, std::size_t iterations = 1) noexcept { static std::string sha256(std::istream &stream, std::size_t iterations = 1) noexcept {
SHA256_CTX context; SHA256_CTX context;
SHA256_Init(&context); SHA256_Init(&context);
@ -184,6 +192,7 @@ namespace SimpleWeb {
return hash; return hash;
} }
/// Returns sha512 hash value from input string.
static std::string sha512(const std::string &input, std::size_t iterations = 1) noexcept { static std::string sha512(const std::string &input, std::size_t iterations = 1) noexcept {
std::string hash; std::string hash;
@ -196,6 +205,7 @@ namespace SimpleWeb {
return hash; return hash;
} }
/// Returns sha512 hash value from input stream.
static std::string sha512(std::istream &stream, std::size_t iterations = 1) noexcept { static std::string sha512(std::istream &stream, std::size_t iterations = 1) noexcept {
SHA512_CTX context; SHA512_CTX context;
SHA512_Init(&context); SHA512_Init(&context);
@ -213,7 +223,19 @@ namespace SimpleWeb {
return hash; 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 { static std::string pbkdf2(const std::string &password, const std::string &salt, int iterations, int key_size) noexcept {
std::string key; std::string key;
key.resize(static_cast<std::size_t>(key_size)); key.resize(static_cast<std::size_t>(key_size));

View file

@ -69,7 +69,7 @@
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
namespace SimpleWeb { namespace SimpleWeb {
/// Defines an annotated interface for mutexes. /// Mutex class that is annotated for Clang Thread Safety Analysis.
class CAPABILITY("mutex") Mutex { class CAPABILITY("mutex") Mutex {
std::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 { class SCOPED_CAPABILITY LockGuard {
Mutex &mutex; Mutex &mutex;
bool locked = true; bool locked = true;

View file

@ -36,6 +36,7 @@ namespace SimpleWeb {
class Session; class Session;
public: 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<Response>, public std::ostream { class Response : public std::enable_shared_from_this<Response>, public std::ostream {
friend class ServerBase<socket_type>; friend class ServerBase<socket_type>;
friend class Server<socket_type>; friend class Server<socket_type>;
@ -124,7 +125,9 @@ namespace SimpleWeb {
return streambuf->size(); 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<void(const error_code &)> &callback = nullptr) noexcept { void send(const std::function<void(const error_code &)> &callback = nullptr) noexcept {
session->connection->set_timeout(timeout_content); session->connection->set_timeout(timeout_content);
@ -138,18 +141,18 @@ namespace SimpleWeb {
send_from_queue(); 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) { void write(const char_type *ptr, std::streamsize n) {
std::ostream::write(ptr, 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()) { void write(StatusCode status_code = StatusCode::success_ok, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
*this << "HTTP/1.1 " << SimpleWeb::status_code(status_code) << "\r\n"; *this << "HTTP/1.1 " << SimpleWeb::status_code(status_code) << "\r\n";
write_header(header, 0); 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()) { void write(StatusCode status_code, string_view content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
*this << "HTTP/1.1 " << SimpleWeb::status_code(status_code) << "\r\n"; *this << "HTTP/1.1 " << SimpleWeb::status_code(status_code) << "\r\n";
write_header(header, content.size()); write_header(header, content.size());
@ -157,7 +160,7 @@ namespace SimpleWeb {
*this << content; *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()) { void write(StatusCode status_code, std::istream &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
*this << "HTTP/1.1 " << SimpleWeb::status_code(status_code) << "\r\n"; *this << "HTTP/1.1 " << SimpleWeb::status_code(status_code) << "\r\n";
content.seekg(0, std::ios::end); content.seekg(0, std::ios::end);
@ -168,22 +171,22 @@ namespace SimpleWeb {
*this << content.rdbuf(); *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()) { void write(string_view content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
write(StatusCode::success_ok, content, header); 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()) { void write(std::istream &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
write(StatusCode::success_ok, content, header); 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) { void write(const CaseInsensitiveMultimap &header) {
write(StatusCode::success_ok, std::string(), 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 /// This is useful when implementing a HTTP/1.0-server sending content
/// without specifying the content length. /// without specifying the content length.
@ -197,7 +200,7 @@ namespace SimpleWeb {
std::size_t size() noexcept { std::size_t size() noexcept {
return streambuf.size(); 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 { std::string string() noexcept {
try { try {
std::string str; std::string str;
@ -233,6 +236,7 @@ namespace SimpleWeb {
CaseInsensitiveMultimap header; CaseInsensitiveMultimap header;
/// The result of the resource regular expression match of the request path.
regex::smatch path_match; regex::smatch path_match;
std::shared_ptr<asio::ip::tcp::endpoint> remote_endpoint; std::shared_ptr<asio::ip::tcp::endpoint> remote_endpoint;
@ -363,16 +367,20 @@ namespace SimpleWeb {
}; };
public: 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 /// Warning: do not add or remove resources after start() is called
std::map<regex_orderable, std::map<std::string, std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>, std::shared_ptr<typename ServerBase<socket_type>::Request>)>>> resource; std::map<regex_orderable, std::map<std::string, std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>, std::shared_ptr<typename ServerBase<socket_type>::Request>)>>> resource;
/// If the request path does not match a resource regex, this function is called.
std::map<std::string, std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>, std::shared_ptr<typename ServerBase<socket_type>::Request>)>> default_resource; std::map<std::string, std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>, std::shared_ptr<typename ServerBase<socket_type>::Request>)>> default_resource;
/// Called when an error occurs.
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Request>, const error_code &)> on_error; std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Request>, const error_code &)> on_error;
/// Called on upgrade requests.
std::function<void(std::unique_ptr<socket_type> &, std::shared_ptr<typename ServerBase<socket_type>::Request>)> on_upgrade; std::function<void(std::unique_ptr<socket_type> &, std::shared_ptr<typename ServerBase<socket_type>::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_context> io_service; std::shared_ptr<io_context> io_service;
/// If you know the server port in advance, use start() instead. /// If you know the server port in advance, use start() instead.
@ -762,6 +770,7 @@ namespace SimpleWeb {
template <> template <>
class Server<HTTP> : public ServerBase<HTTP> { class Server<HTTP> : public ServerBase<HTTP> {
public: public:
/// Constructs a server object.
Server() noexcept : ServerBase<HTTP>::ServerBase(80) {} Server() noexcept : ServerBase<HTTP>::ServerBase(80) {}
protected: protected:

View file

@ -20,9 +20,16 @@ namespace SimpleWeb {
bool set_session_id_context = false; bool set_session_id_context = false;
public: 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<HTTPS>::ServerBase(443), context(asio::ssl::context::tlsv12) { : ServerBase<HTTPS>::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); context.use_private_key_file(private_key_file, asio::ssl::context::pem);
if(verify_file.size() > 0) { if(verify_file.size() > 0) {

View file

@ -151,7 +151,7 @@ namespace SimpleWeb {
class HttpHeader { class HttpHeader {
public: public:
/// Parse header fields /// Parse header fields from stream
static CaseInsensitiveMultimap parse(std::istream &stream) noexcept { static CaseInsensitiveMultimap parse(std::istream &stream) noexcept {
CaseInsensitiveMultimap result; CaseInsensitiveMultimap result;
std::string line; std::string line;
@ -170,36 +170,37 @@ namespace SimpleWeb {
public: public:
class SemicolonSeparatedAttributes { class SemicolonSeparatedAttributes {
public: public:
/// Parse Set-Cookie or Content-Disposition header field value. Attribute values are percent-decoded. /// Parse Set-Cookie or Content-Disposition from given header field value.
static CaseInsensitiveMultimap parse(const std::string &str) { /// Attribute values are percent-decoded.
static CaseInsensitiveMultimap parse(const std::string &value) {
CaseInsensitiveMultimap result; CaseInsensitiveMultimap result;
std::size_t name_start_pos = std::string::npos; std::size_t name_start_pos = std::string::npos;
std::size_t name_end_pos = std::string::npos; std::size_t name_end_pos = std::string::npos;
std::size_t value_start_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(name_start_pos == std::string::npos) {
if(str[c] != ' ' && str[c] != ';') if(value[c] != ' ' && value[c] != ';')
name_start_pos = c; name_start_pos = c;
} }
else { else {
if(name_end_pos == std::string::npos) { if(name_end_pos == std::string::npos) {
if(str[c] == ';') { if(value[c] == ';') {
result.emplace(str.substr(name_start_pos, c - name_start_pos), std::string()); result.emplace(value.substr(name_start_pos, c - name_start_pos), std::string());
name_start_pos = std::string::npos; name_start_pos = std::string::npos;
} }
else if(str[c] == '=') else if(value[c] == '=')
name_end_pos = c; name_end_pos = c;
} }
else { else {
if(value_start_pos == std::string::npos) { 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; value_start_pos = c + 1;
else else
value_start_pos = c; value_start_pos = c;
} }
else if(str[c] == '"' || str[c] == ';') { else if(value[c] == '"' || value[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))); 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_start_pos = std::string::npos;
name_end_pos = std::string::npos; name_end_pos = std::string::npos;
value_start_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_start_pos != std::string::npos) {
if(name_end_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) { else if(value_start_pos != std::string::npos) {
if(str.back() == '"') if(value.back() == '"')
result.emplace(str.substr(name_start_pos, name_end_pos - name_start_pos), Percent::decode(str.substr(value_start_pos, str.size() - 1))); result.emplace(value.substr(name_start_pos, name_end_pos - name_start_pos), Percent::decode(value.substr(value_start_pos, value.size() - 1)));
else 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 { class RequestMessage {
public: 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 { 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::string line;
std::size_t method_end; std::size_t method_end;
@ -273,7 +284,15 @@ namespace SimpleWeb {
class ResponseMessage { class ResponseMessage {
public: 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 { static bool parse(std::istream &stream, std::string &version, std::string &status_code, CaseInsensitiveMultimap &header) noexcept {
std::string line; std::string line;
std::size_t version_end; std::size_t version_end;
@ -314,9 +333,9 @@ namespace SimpleWeb {
#endif #endif
namespace SimpleWeb { 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 { 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<long> count; std::atomic<long> count;
public: public:
@ -335,7 +354,8 @@ namespace SimpleWeb {
ScopeRunner() noexcept : count(0) {} 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<SharedLock> continue_lock() noexcept { std::unique_ptr<SharedLock> continue_lock() noexcept {
long expected = count; long expected = count;
while(expected >= 0 && !count.compare_exchange_weak(expected, expected + 1)) while(expected >= 0 && !count.compare_exchange_weak(expected, expected + 1))
@ -347,7 +367,7 @@ namespace SimpleWeb {
return std::unique_ptr<SharedLock>(new SharedLock(count)); return std::unique_ptr<SharedLock>(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 { void stop() noexcept {
long expected = 0; long expected = 0;
while(!count.compare_exchange_weak(expected, -1)) { while(!count.compare_exchange_weak(expected, -1)) {