diff options
author | 2016-10-07 14:13:19 +0200 | |
---|---|---|
committer | 2016-10-07 14:13:19 +0200 | |
commit | ff01b716711b97c2fcaa709ea97f7650f106aa10 (patch) | |
tree | 50dd4d687f38f50c4e136af030c02c267c769f3a /3rdparty/asio/src/examples/cpp03/http | |
parent | 2a138159c30457aecd9e9679be5159704db0f954 (diff) |
Added ASIO networking library (nw)
Diffstat (limited to '3rdparty/asio/src/examples/cpp03/http')
73 files changed, 6333 insertions, 0 deletions
diff --git a/3rdparty/asio/src/examples/cpp03/http/client/.gitignore b/3rdparty/asio/src/examples/cpp03/http/client/.gitignore new file mode 100644 index 00000000000..e80f510d76c --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/client/.gitignore @@ -0,0 +1,10 @@ +.deps +.dirstamp +*.o +*.obj +*.exe +*_client +*.ilk +*.manifest +*.pdb +*.tds diff --git a/3rdparty/asio/src/examples/cpp03/http/client/async_client.cpp b/3rdparty/asio/src/examples/cpp03/http/client/async_client.cpp new file mode 100644 index 00000000000..83dcc36d53a --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/client/async_client.cpp @@ -0,0 +1,204 @@ +// +// async_client.cpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <iostream> +#include <istream> +#include <ostream> +#include <string> +#include <asio.hpp> +#include <boost/bind.hpp> + +using asio::ip::tcp; + +class client +{ +public: + client(asio::io_context& io_context, + const std::string& server, const std::string& path) + : resolver_(io_context), + socket_(io_context) + { + // Form the request. We specify the "Connection: close" header so that the + // server will close the socket after transmitting the response. This will + // allow us to treat all data up until the EOF as the content. + std::ostream request_stream(&request_); + request_stream << "GET " << path << " HTTP/1.0\r\n"; + request_stream << "Host: " << server << "\r\n"; + request_stream << "Accept: */*\r\n"; + request_stream << "Connection: close\r\n\r\n"; + + // Start an asynchronous resolve to translate the server and service names + // into a list of endpoints. + resolver_.async_resolve(server, "http", + boost::bind(&client::handle_resolve, this, + asio::placeholders::error, + asio::placeholders::results)); + } + +private: + void handle_resolve(const asio::error_code& err, + const tcp::resolver::results_type& endpoints) + { + if (!err) + { + // Attempt a connection to each endpoint in the list until we + // successfully establish a connection. + asio::async_connect(socket_, endpoints, + boost::bind(&client::handle_connect, this, + asio::placeholders::error)); + } + else + { + std::cout << "Error: " << err.message() << "\n"; + } + } + + void handle_connect(const asio::error_code& err) + { + if (!err) + { + // The connection was successful. Send the request. + asio::async_write(socket_, request_, + boost::bind(&client::handle_write_request, this, + asio::placeholders::error)); + } + else + { + std::cout << "Error: " << err.message() << "\n"; + } + } + + void handle_write_request(const asio::error_code& err) + { + if (!err) + { + // Read the response status line. The response_ streambuf will + // automatically grow to accommodate the entire line. The growth may be + // limited by passing a maximum size to the streambuf constructor. + asio::async_read_until(socket_, response_, "\r\n", + boost::bind(&client::handle_read_status_line, this, + asio::placeholders::error)); + } + else + { + std::cout << "Error: " << err.message() << "\n"; + } + } + + void handle_read_status_line(const asio::error_code& err) + { + if (!err) + { + // Check that response is OK. + std::istream response_stream(&response_); + std::string http_version; + response_stream >> http_version; + unsigned int status_code; + response_stream >> status_code; + std::string status_message; + std::getline(response_stream, status_message); + if (!response_stream || http_version.substr(0, 5) != "HTTP/") + { + std::cout << "Invalid response\n"; + return; + } + if (status_code != 200) + { + std::cout << "Response returned with status code "; + std::cout << status_code << "\n"; + return; + } + + // Read the response headers, which are terminated by a blank line. + asio::async_read_until(socket_, response_, "\r\n\r\n", + boost::bind(&client::handle_read_headers, this, + asio::placeholders::error)); + } + else + { + std::cout << "Error: " << err << "\n"; + } + } + + void handle_read_headers(const asio::error_code& err) + { + if (!err) + { + // Process the response headers. + std::istream response_stream(&response_); + std::string header; + while (std::getline(response_stream, header) && header != "\r") + std::cout << header << "\n"; + std::cout << "\n"; + + // Write whatever content we already have to output. + if (response_.size() > 0) + std::cout << &response_; + + // Start reading remaining data until EOF. + asio::async_read(socket_, response_, + asio::transfer_at_least(1), + boost::bind(&client::handle_read_content, this, + asio::placeholders::error)); + } + else + { + std::cout << "Error: " << err << "\n"; + } + } + + void handle_read_content(const asio::error_code& err) + { + if (!err) + { + // Write all of the data that has been read so far. + std::cout << &response_; + + // Continue reading remaining data until EOF. + asio::async_read(socket_, response_, + asio::transfer_at_least(1), + boost::bind(&client::handle_read_content, this, + asio::placeholders::error)); + } + else if (err != asio::error::eof) + { + std::cout << "Error: " << err << "\n"; + } + } + + tcp::resolver resolver_; + tcp::socket socket_; + asio::streambuf request_; + asio::streambuf response_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cout << "Usage: async_client <server> <path>\n"; + std::cout << "Example:\n"; + std::cout << " async_client www.boost.org /LICENSE_1_0.txt\n"; + return 1; + } + + asio::io_context io_context; + client c(io_context, argv[1], argv[2]); + io_context.run(); + } + catch (std::exception& e) + { + std::cout << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/3rdparty/asio/src/examples/cpp03/http/client/sync_client.cpp b/3rdparty/asio/src/examples/cpp03/http/client/sync_client.cpp new file mode 100644 index 00000000000..a11b5c7c76a --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/client/sync_client.cpp @@ -0,0 +1,106 @@ +// +// sync_client.cpp +// ~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <iostream> +#include <istream> +#include <ostream> +#include <string> +#include <asio.hpp> + +using asio::ip::tcp; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 3) + { + std::cout << "Usage: sync_client <server> <path>\n"; + std::cout << "Example:\n"; + std::cout << " sync_client www.boost.org /LICENSE_1_0.txt\n"; + return 1; + } + + asio::io_context io_context; + + // Get a list of endpoints corresponding to the server name. + tcp::resolver resolver(io_context); + tcp::resolver::results_type endpoints = resolver.resolve(argv[1], "http"); + + // Try each endpoint until we successfully establish a connection. + tcp::socket socket(io_context); + asio::connect(socket, endpoints); + + // Form the request. We specify the "Connection: close" header so that the + // server will close the socket after transmitting the response. This will + // allow us to treat all data up until the EOF as the content. + asio::streambuf request; + std::ostream request_stream(&request); + request_stream << "GET " << argv[2] << " HTTP/1.0\r\n"; + request_stream << "Host: " << argv[1] << "\r\n"; + request_stream << "Accept: */*\r\n"; + request_stream << "Connection: close\r\n\r\n"; + + // Send the request. + asio::write(socket, request); + + // Read the response status line. The response streambuf will automatically + // grow to accommodate the entire line. The growth may be limited by passing + // a maximum size to the streambuf constructor. + asio::streambuf response; + asio::read_until(socket, response, "\r\n"); + + // Check that response is OK. + std::istream response_stream(&response); + std::string http_version; + response_stream >> http_version; + unsigned int status_code; + response_stream >> status_code; + std::string status_message; + std::getline(response_stream, status_message); + if (!response_stream || http_version.substr(0, 5) != "HTTP/") + { + std::cout << "Invalid response\n"; + return 1; + } + if (status_code != 200) + { + std::cout << "Response returned with status code " << status_code << "\n"; + return 1; + } + + // Read the response headers, which are terminated by a blank line. + asio::read_until(socket, response, "\r\n\r\n"); + + // Process the response headers. + std::string header; + while (std::getline(response_stream, header) && header != "\r") + std::cout << header << "\n"; + std::cout << "\n"; + + // Write whatever content we already have to output. + if (response.size() > 0) + std::cout << &response; + + // Read until EOF, writing data to output as we go. + asio::error_code error; + while (asio::read(socket, response, + asio::transfer_at_least(1), error)) + std::cout << &response; + if (error != asio::error::eof) + throw asio::system_error(error); + } + catch (std::exception& e) + { + std::cout << "Exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/3rdparty/asio/src/examples/cpp03/http/doc_root/data_1K.html b/3rdparty/asio/src/examples/cpp03/http/doc_root/data_1K.html new file mode 100644 index 00000000000..1372c4dd177 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/doc_root/data_1K.html @@ -0,0 +1,28 @@ +<!-- +Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +--> + +<html> +<body> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the<br/> +</body> +</html> + +<!-- boostinspect:nounlinked --> diff --git a/3rdparty/asio/src/examples/cpp03/http/doc_root/data_2K.html b/3rdparty/asio/src/examples/cpp03/http/doc_root/data_2K.html new file mode 100644 index 00000000000..686ce52b0a0 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/doc_root/data_2K.html @@ -0,0 +1,49 @@ +<!-- +Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +--> + +<html> +<body> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps ove<br/> +</body> +</html> + +<!-- boostinspect:nounlinked --> diff --git a/3rdparty/asio/src/examples/cpp03/http/doc_root/data_4K.html b/3rdparty/asio/src/examples/cpp03/http/doc_root/data_4K.html new file mode 100644 index 00000000000..f5db0a2bc00 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/doc_root/data_4K.html @@ -0,0 +1,91 @@ +<!-- +Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +--> + +<html> +<body> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox<br/> +</body> +</html> + +<!-- boostinspect:nounlinked --> diff --git a/3rdparty/asio/src/examples/cpp03/http/doc_root/data_8K.html b/3rdparty/asio/src/examples/cpp03/http/doc_root/data_8K.html new file mode 100644 index 00000000000..0d42f72886b --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/doc_root/data_8K.html @@ -0,0 +1,175 @@ +<!-- +Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +--> + +<html> +<body> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +The quick brown fox jumps over the lazy dog<br/> +.... +</body> +</html> + +<!-- boostinspect:nounlinked --> diff --git a/3rdparty/asio/src/examples/cpp03/http/server/.gitignore b/3rdparty/asio/src/examples/cpp03/http/server/.gitignore new file mode 100644 index 00000000000..0882fa6ced3 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server/.gitignore @@ -0,0 +1,11 @@ +.deps +.dirstamp +*.o +*.obj +*.exe +*_server +*_client +*.ilk +*.manifest +*.pdb +*.tds diff --git a/3rdparty/asio/src/examples/cpp03/http/server/connection.cpp b/3rdparty/asio/src/examples/cpp03/http/server/connection.cpp new file mode 100644 index 00000000000..acbf637b453 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server/connection.cpp @@ -0,0 +1,99 @@ +// +// connection.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "connection.hpp" +#include <vector> +#include <boost/bind.hpp> +#include "connection_manager.hpp" +#include "request_handler.hpp" + +namespace http { +namespace server { + +connection::connection(asio::io_context& io_context, + connection_manager& manager, request_handler& handler) + : socket_(io_context), + connection_manager_(manager), + request_handler_(handler) +{ +} + +asio::ip::tcp::socket& connection::socket() +{ + return socket_; +} + +void connection::start() +{ + socket_.async_read_some(asio::buffer(buffer_), + boost::bind(&connection::handle_read, shared_from_this(), + asio::placeholders::error, + asio::placeholders::bytes_transferred)); +} + +void connection::stop() +{ + socket_.close(); +} + +void connection::handle_read(const asio::error_code& e, + std::size_t bytes_transferred) +{ + if (!e) + { + boost::tribool result; + boost::tie(result, boost::tuples::ignore) = request_parser_.parse( + request_, buffer_.data(), buffer_.data() + bytes_transferred); + + if (result) + { + request_handler_.handle_request(request_, reply_); + asio::async_write(socket_, reply_.to_buffers(), + boost::bind(&connection::handle_write, shared_from_this(), + asio::placeholders::error)); + } + else if (!result) + { + reply_ = reply::stock_reply(reply::bad_request); + asio::async_write(socket_, reply_.to_buffers(), + boost::bind(&connection::handle_write, shared_from_this(), + asio::placeholders::error)); + } + else + { + socket_.async_read_some(asio::buffer(buffer_), + boost::bind(&connection::handle_read, shared_from_this(), + asio::placeholders::error, + asio::placeholders::bytes_transferred)); + } + } + else if (e != asio::error::operation_aborted) + { + connection_manager_.stop(shared_from_this()); + } +} + +void connection::handle_write(const asio::error_code& e) +{ + if (!e) + { + // Initiate graceful connection closure. + asio::error_code ignored_ec; + socket_.shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec); + } + + if (e != asio::error::operation_aborted) + { + connection_manager_.stop(shared_from_this()); + } +} + +} // namespace server +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server/connection.hpp b/3rdparty/asio/src/examples/cpp03/http/server/connection.hpp new file mode 100644 index 00000000000..6193b486443 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server/connection.hpp @@ -0,0 +1,83 @@ +// +// connection.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_CONNECTION_HPP +#define HTTP_CONNECTION_HPP + +#include <asio.hpp> +#include <boost/array.hpp> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include "reply.hpp" +#include "request.hpp" +#include "request_handler.hpp" +#include "request_parser.hpp" + +namespace http { +namespace server { + +class connection_manager; + +/// Represents a single connection from a client. +class connection + : public boost::enable_shared_from_this<connection>, + private boost::noncopyable +{ +public: + /// Construct a connection with the given io_context. + explicit connection(asio::io_context& io_context, + connection_manager& manager, request_handler& handler); + + /// Get the socket associated with the connection. + asio::ip::tcp::socket& socket(); + + /// Start the first asynchronous operation for the connection. + void start(); + + /// Stop all asynchronous operations associated with the connection. + void stop(); + +private: + /// Handle completion of a read operation. + void handle_read(const asio::error_code& e, + std::size_t bytes_transferred); + + /// Handle completion of a write operation. + void handle_write(const asio::error_code& e); + + /// Socket for the connection. + asio::ip::tcp::socket socket_; + + /// The manager for this connection. + connection_manager& connection_manager_; + + /// The handler used to process the incoming request. + request_handler& request_handler_; + + /// Buffer for incoming data. + boost::array<char, 8192> buffer_; + + /// The incoming request. + request request_; + + /// The parser for the incoming request. + request_parser request_parser_; + + /// The reply to be sent back to the client. + reply reply_; +}; + +typedef boost::shared_ptr<connection> connection_ptr; + +} // namespace server +} // namespace http + +#endif // HTTP_CONNECTION_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server/connection_manager.cpp b/3rdparty/asio/src/examples/cpp03/http/server/connection_manager.cpp new file mode 100644 index 00000000000..8fc9d8f285b --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server/connection_manager.cpp @@ -0,0 +1,38 @@ +// +// connection_manager.cpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "connection_manager.hpp" +#include <algorithm> +#include <boost/bind.hpp> + +namespace http { +namespace server { + +void connection_manager::start(connection_ptr c) +{ + connections_.insert(c); + c->start(); +} + +void connection_manager::stop(connection_ptr c) +{ + connections_.erase(c); + c->stop(); +} + +void connection_manager::stop_all() +{ + std::for_each(connections_.begin(), connections_.end(), + boost::bind(&connection::stop, _1)); + connections_.clear(); +} + +} // namespace server +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server/connection_manager.hpp b/3rdparty/asio/src/examples/cpp03/http/server/connection_manager.hpp new file mode 100644 index 00000000000..e67653f6a26 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server/connection_manager.hpp @@ -0,0 +1,44 @@ +// +// connection_manager.hpp +// ~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_CONNECTION_MANAGER_HPP +#define HTTP_CONNECTION_MANAGER_HPP + +#include <set> +#include <boost/noncopyable.hpp> +#include "connection.hpp" + +namespace http { +namespace server { + +/// Manages open connections so that they may be cleanly stopped when the server +/// needs to shut down. +class connection_manager + : private boost::noncopyable +{ +public: + /// Add the specified connection to the manager and start it. + void start(connection_ptr c); + + /// Stop the specified connection. + void stop(connection_ptr c); + + /// Stop all connections. + void stop_all(); + +private: + /// The managed connections. + std::set<connection_ptr> connections_; +}; + +} // namespace server +} // namespace http + +#endif // HTTP_CONNECTION_MANAGER_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server/header.hpp b/3rdparty/asio/src/examples/cpp03/http/server/header.hpp new file mode 100644 index 00000000000..22e3c4a7a10 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server/header.hpp @@ -0,0 +1,28 @@ +// +// header.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_HEADER_HPP +#define HTTP_HEADER_HPP + +#include <string> + +namespace http { +namespace server { + +struct header +{ + std::string name; + std::string value; +}; + +} // namespace server +} // namespace http + +#endif // HTTP_HEADER_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server/main.cpp b/3rdparty/asio/src/examples/cpp03/http/server/main.cpp new file mode 100644 index 00000000000..32d6f54dcb5 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server/main.cpp @@ -0,0 +1,44 @@ +// +// main.cpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <iostream> +#include <string> +#include <asio.hpp> +#include <boost/bind.hpp> +#include "server.hpp" + +int main(int argc, char* argv[]) +{ + try + { + // Check command line arguments. + if (argc != 4) + { + std::cerr << "Usage: http_server <address> <port> <doc_root>\n"; + std::cerr << " For IPv4, try:\n"; + std::cerr << " receiver 0.0.0.0 80 .\n"; + std::cerr << " For IPv6, try:\n"; + std::cerr << " receiver 0::0 80 .\n"; + return 1; + } + + // Initialise the server. + http::server::server s(argv[1], argv[2], argv[3]); + + // Run the server until stopped. + s.run(); + } + catch (std::exception& e) + { + std::cerr << "exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/3rdparty/asio/src/examples/cpp03/http/server/mime_types.cpp b/3rdparty/asio/src/examples/cpp03/http/server/mime_types.cpp new file mode 100644 index 00000000000..80fdfe30e64 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server/mime_types.cpp @@ -0,0 +1,46 @@ +// +// mime_types.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "mime_types.hpp" + +namespace http { +namespace server { +namespace mime_types { + +struct mapping +{ + const char* extension; + const char* mime_type; +} mappings[] = +{ + { "gif", "image/gif" }, + { "htm", "text/html" }, + { "html", "text/html" }, + { "jpg", "image/jpeg" }, + { "png", "image/png" }, + { 0, 0 } // Marks end of list. +}; + +std::string extension_to_type(const std::string& extension) +{ + for (mapping* m = mappings; m->extension; ++m) + { + if (m->extension == extension) + { + return m->mime_type; + } + } + + return "text/plain"; +} + +} // namespace mime_types +} // namespace server +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server/mime_types.hpp b/3rdparty/asio/src/examples/cpp03/http/server/mime_types.hpp new file mode 100644 index 00000000000..2fc0332791f --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server/mime_types.hpp @@ -0,0 +1,27 @@ +// +// mime_types.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_MIME_TYPES_HPP +#define HTTP_MIME_TYPES_HPP + +#include <string> + +namespace http { +namespace server { +namespace mime_types { + +/// Convert a file extension into a MIME type. +std::string extension_to_type(const std::string& extension); + +} // namespace mime_types +} // namespace server +} // namespace http + +#endif // HTTP_MIME_TYPES_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server/reply.cpp b/3rdparty/asio/src/examples/cpp03/http/server/reply.cpp new file mode 100644 index 00000000000..f3730d3d94d --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server/reply.cpp @@ -0,0 +1,256 @@ +// +// reply.cpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "reply.hpp" +#include <string> +#include <boost/lexical_cast.hpp> + +namespace http { +namespace server { + +namespace status_strings { + +const std::string ok = + "HTTP/1.0 200 OK\r\n"; +const std::string created = + "HTTP/1.0 201 Created\r\n"; +const std::string accepted = + "HTTP/1.0 202 Accepted\r\n"; +const std::string no_content = + "HTTP/1.0 204 No Content\r\n"; +const std::string multiple_choices = + "HTTP/1.0 300 Multiple Choices\r\n"; +const std::string moved_permanently = + "HTTP/1.0 301 Moved Permanently\r\n"; +const std::string moved_temporarily = + "HTTP/1.0 302 Moved Temporarily\r\n"; +const std::string not_modified = + "HTTP/1.0 304 Not Modified\r\n"; +const std::string bad_request = + "HTTP/1.0 400 Bad Request\r\n"; +const std::string unauthorized = + "HTTP/1.0 401 Unauthorized\r\n"; +const std::string forbidden = + "HTTP/1.0 403 Forbidden\r\n"; +const std::string not_found = + "HTTP/1.0 404 Not Found\r\n"; +const std::string internal_server_error = + "HTTP/1.0 500 Internal Server Error\r\n"; +const std::string not_implemented = + "HTTP/1.0 501 Not Implemented\r\n"; +const std::string bad_gateway = + "HTTP/1.0 502 Bad Gateway\r\n"; +const std::string service_unavailable = + "HTTP/1.0 503 Service Unavailable\r\n"; + +asio::const_buffer to_buffer(reply::status_type status) +{ + switch (status) + { + case reply::ok: + return asio::buffer(ok); + case reply::created: + return asio::buffer(created); + case reply::accepted: + return asio::buffer(accepted); + case reply::no_content: + return asio::buffer(no_content); + case reply::multiple_choices: + return asio::buffer(multiple_choices); + case reply::moved_permanently: + return asio::buffer(moved_permanently); + case reply::moved_temporarily: + return asio::buffer(moved_temporarily); + case reply::not_modified: + return asio::buffer(not_modified); + case reply::bad_request: + return asio::buffer(bad_request); + case reply::unauthorized: + return asio::buffer(unauthorized); + case reply::forbidden: + return asio::buffer(forbidden); + case reply::not_found: + return asio::buffer(not_found); + case reply::internal_server_error: + return asio::buffer(internal_server_error); + case reply::not_implemented: + return asio::buffer(not_implemented); + case reply::bad_gateway: + return asio::buffer(bad_gateway); + case reply::service_unavailable: + return asio::buffer(service_unavailable); + default: + return asio::buffer(internal_server_error); + } +} + +} // namespace status_strings + +namespace misc_strings { + +const char name_value_separator[] = { ':', ' ' }; +const char crlf[] = { '\r', '\n' }; + +} // namespace misc_strings + +std::vector<asio::const_buffer> reply::to_buffers() +{ + std::vector<asio::const_buffer> buffers; + buffers.push_back(status_strings::to_buffer(status)); + for (std::size_t i = 0; i < headers.size(); ++i) + { + header& h = headers[i]; + buffers.push_back(asio::buffer(h.name)); + buffers.push_back(asio::buffer(misc_strings::name_value_separator)); + buffers.push_back(asio::buffer(h.value)); + buffers.push_back(asio::buffer(misc_strings::crlf)); + } + buffers.push_back(asio::buffer(misc_strings::crlf)); + buffers.push_back(asio::buffer(content)); + return buffers; +} + +namespace stock_replies { + +const char ok[] = ""; +const char created[] = + "<html>" + "<head><title>Created</title></head>" + "<body><h1>201 Created</h1></body>" + "</html>"; +const char accepted[] = + "<html>" + "<head><title>Accepted</title></head>" + "<body><h1>202 Accepted</h1></body>" + "</html>"; +const char no_content[] = + "<html>" + "<head><title>No Content</title></head>" + "<body><h1>204 Content</h1></body>" + "</html>"; +const char multiple_choices[] = + "<html>" + "<head><title>Multiple Choices</title></head>" + "<body><h1>300 Multiple Choices</h1></body>" + "</html>"; +const char moved_permanently[] = + "<html>" + "<head><title>Moved Permanently</title></head>" + "<body><h1>301 Moved Permanently</h1></body>" + "</html>"; +const char moved_temporarily[] = + "<html>" + "<head><title>Moved Temporarily</title></head>" + "<body><h1>302 Moved Temporarily</h1></body>" + "</html>"; +const char not_modified[] = + "<html>" + "<head><title>Not Modified</title></head>" + "<body><h1>304 Not Modified</h1></body>" + "</html>"; +const char bad_request[] = + "<html>" + "<head><title>Bad Request</title></head>" + "<body><h1>400 Bad Request</h1></body>" + "</html>"; +const char unauthorized[] = + "<html>" + "<head><title>Unauthorized</title></head>" + "<body><h1>401 Unauthorized</h1></body>" + "</html>"; +const char forbidden[] = + "<html>" + "<head><title>Forbidden</title></head>" + "<body><h1>403 Forbidden</h1></body>" + "</html>"; +const char not_found[] = + "<html>" + "<head><title>Not Found</title></head>" + "<body><h1>404 Not Found</h1></body>" + "</html>"; +const char internal_server_error[] = + "<html>" + "<head><title>Internal Server Error</title></head>" + "<body><h1>500 Internal Server Error</h1></body>" + "</html>"; +const char not_implemented[] = + "<html>" + "<head><title>Not Implemented</title></head>" + "<body><h1>501 Not Implemented</h1></body>" + "</html>"; +const char bad_gateway[] = + "<html>" + "<head><title>Bad Gateway</title></head>" + "<body><h1>502 Bad Gateway</h1></body>" + "</html>"; +const char service_unavailable[] = + "<html>" + "<head><title>Service Unavailable</title></head>" + "<body><h1>503 Service Unavailable</h1></body>" + "</html>"; + +std::string to_string(reply::status_type status) +{ + switch (status) + { + case reply::ok: + return ok; + case reply::created: + return created; + case reply::accepted: + return accepted; + case reply::no_content: + return no_content; + case reply::multiple_choices: + return multiple_choices; + case reply::moved_permanently: + return moved_permanently; + case reply::moved_temporarily: + return moved_temporarily; + case reply::not_modified: + return not_modified; + case reply::bad_request: + return bad_request; + case reply::unauthorized: + return unauthorized; + case reply::forbidden: + return forbidden; + case reply::not_found: + return not_found; + case reply::internal_server_error: + return internal_server_error; + case reply::not_implemented: + return not_implemented; + case reply::bad_gateway: + return bad_gateway; + case reply::service_unavailable: + return service_unavailable; + default: + return internal_server_error; + } +} + +} // namespace stock_replies + +reply reply::stock_reply(reply::status_type status) +{ + reply rep; + rep.status = status; + rep.content = stock_replies::to_string(status); + rep.headers.resize(2); + rep.headers[0].name = "Content-Length"; + rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size()); + rep.headers[1].name = "Content-Type"; + rep.headers[1].value = "text/html"; + return rep; +} + +} // namespace server +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server/reply.hpp b/3rdparty/asio/src/examples/cpp03/http/server/reply.hpp new file mode 100644 index 00000000000..5189ac4363e --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server/reply.hpp @@ -0,0 +1,64 @@ +// +// reply.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_REPLY_HPP +#define HTTP_REPLY_HPP + +#include <string> +#include <vector> +#include <asio.hpp> +#include "header.hpp" + +namespace http { +namespace server { + +/// A reply to be sent to a client. +struct reply +{ + /// The status of the reply. + enum status_type + { + ok = 200, + created = 201, + accepted = 202, + no_content = 204, + multiple_choices = 300, + moved_permanently = 301, + moved_temporarily = 302, + not_modified = 304, + bad_request = 400, + unauthorized = 401, + forbidden = 403, + not_found = 404, + internal_server_error = 500, + not_implemented = 501, + bad_gateway = 502, + service_unavailable = 503 + } status; + + /// The headers to be included in the reply. + std::vector<header> headers; + + /// The content to be sent in the reply. + std::string content; + + /// Convert the reply into a vector of buffers. The buffers do not own the + /// underlying memory blocks, therefore the reply object must remain valid and + /// not be changed until the write operation has completed. + std::vector<asio::const_buffer> to_buffers(); + + /// Get a stock reply. + static reply stock_reply(status_type status); +}; + +} // namespace server +} // namespace http + +#endif // HTTP_REPLY_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server/request.hpp b/3rdparty/asio/src/examples/cpp03/http/server/request.hpp new file mode 100644 index 00000000000..129a3990b3d --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server/request.hpp @@ -0,0 +1,34 @@ +// +// request.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_REQUEST_HPP +#define HTTP_REQUEST_HPP + +#include <string> +#include <vector> +#include "header.hpp" + +namespace http { +namespace server { + +/// A request received from a client. +struct request +{ + std::string method; + std::string uri; + int http_version_major; + int http_version_minor; + std::vector<header> headers; +}; + +} // namespace server +} // namespace http + +#endif // HTTP_REQUEST_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server/request_handler.cpp b/3rdparty/asio/src/examples/cpp03/http/server/request_handler.cpp new file mode 100644 index 00000000000..1201ef109d2 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server/request_handler.cpp @@ -0,0 +1,122 @@ +// +// request_handler.cpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "request_handler.hpp" +#include <fstream> +#include <sstream> +#include <string> +#include <boost/lexical_cast.hpp> +#include "mime_types.hpp" +#include "reply.hpp" +#include "request.hpp" + +namespace http { +namespace server { + +request_handler::request_handler(const std::string& doc_root) + : doc_root_(doc_root) +{ +} + +void request_handler::handle_request(const request& req, reply& rep) +{ + // Decode url to path. + std::string request_path; + if (!url_decode(req.uri, request_path)) + { + rep = reply::stock_reply(reply::bad_request); + return; + } + + // Request path must be absolute and not contain "..". + if (request_path.empty() || request_path[0] != '/' + || request_path.find("..") != std::string::npos) + { + rep = reply::stock_reply(reply::bad_request); + return; + } + + // If path ends in slash (i.e. is a directory) then add "index.html". + if (request_path[request_path.size() - 1] == '/') + { + request_path += "index.html"; + } + + // Determine the file extension. + std::size_t last_slash_pos = request_path.find_last_of("/"); + std::size_t last_dot_pos = request_path.find_last_of("."); + std::string extension; + if (last_dot_pos != std::string::npos && last_dot_pos > last_slash_pos) + { + extension = request_path.substr(last_dot_pos + 1); + } + + // Open the file to send back. + std::string full_path = doc_root_ + request_path; + std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary); + if (!is) + { + rep = reply::stock_reply(reply::not_found); + return; + } + + // Fill out the reply to be sent to the client. + rep.status = reply::ok; + char buf[512]; + while (is.read(buf, sizeof(buf)).gcount() > 0) + rep.content.append(buf, is.gcount()); + rep.headers.resize(2); + rep.headers[0].name = "Content-Length"; + rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size()); + rep.headers[1].name = "Content-Type"; + rep.headers[1].value = mime_types::extension_to_type(extension); +} + +bool request_handler::url_decode(const std::string& in, std::string& out) +{ + out.clear(); + out.reserve(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) + { + if (in[i] == '%') + { + if (i + 3 <= in.size()) + { + int value = 0; + std::istringstream is(in.substr(i + 1, 2)); + if (is >> std::hex >> value) + { + out += static_cast<char>(value); + i += 2; + } + else + { + return false; + } + } + else + { + return false; + } + } + else if (in[i] == '+') + { + out += ' '; + } + else + { + out += in[i]; + } + } + return true; +} + +} // namespace server +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server/request_handler.hpp b/3rdparty/asio/src/examples/cpp03/http/server/request_handler.hpp new file mode 100644 index 00000000000..56a16dd395a --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server/request_handler.hpp @@ -0,0 +1,46 @@ +// +// request_handler.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_REQUEST_HANDLER_HPP +#define HTTP_REQUEST_HANDLER_HPP + +#include <string> +#include <boost/noncopyable.hpp> + +namespace http { +namespace server { + +struct reply; +struct request; + +/// The common handler for all incoming requests. +class request_handler + : private boost::noncopyable +{ +public: + /// Construct with a directory containing files to be served. + explicit request_handler(const std::string& doc_root); + + /// Handle a request and produce a reply. + void handle_request(const request& req, reply& rep); + +private: + /// The directory containing the files to be served. + std::string doc_root_; + + /// Perform URL-decoding on a string. Returns false if the encoding was + /// invalid. + static bool url_decode(const std::string& in, std::string& out); +}; + +} // namespace server +} // namespace http + +#endif // HTTP_REQUEST_HANDLER_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server/request_parser.cpp b/3rdparty/asio/src/examples/cpp03/http/server/request_parser.cpp new file mode 100644 index 00000000000..80644eea529 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server/request_parser.cpp @@ -0,0 +1,315 @@ +// +// request_parser.cpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "request_parser.hpp" +#include "request.hpp" + +namespace http { +namespace server { + +request_parser::request_parser() + : state_(method_start) +{ +} + +void request_parser::reset() +{ + state_ = method_start; +} + +boost::tribool request_parser::consume(request& req, char input) +{ + switch (state_) + { + case method_start: + if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + state_ = method; + req.method.push_back(input); + return boost::indeterminate; + } + case method: + if (input == ' ') + { + state_ = uri; + return boost::indeterminate; + } + else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + req.method.push_back(input); + return boost::indeterminate; + } + case uri: + if (input == ' ') + { + state_ = http_version_h; + return boost::indeterminate; + } + else if (is_ctl(input)) + { + return false; + } + else + { + req.uri.push_back(input); + return boost::indeterminate; + } + case http_version_h: + if (input == 'H') + { + state_ = http_version_t_1; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_t_1: + if (input == 'T') + { + state_ = http_version_t_2; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_t_2: + if (input == 'T') + { + state_ = http_version_p; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_p: + if (input == 'P') + { + state_ = http_version_slash; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_slash: + if (input == '/') + { + req.http_version_major = 0; + req.http_version_minor = 0; + state_ = http_version_major_start; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_major_start: + if (is_digit(input)) + { + req.http_version_major = req.http_version_major * 10 + input - '0'; + state_ = http_version_major; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_major: + if (input == '.') + { + state_ = http_version_minor_start; + return boost::indeterminate; + } + else if (is_digit(input)) + { + req.http_version_major = req.http_version_major * 10 + input - '0'; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_minor_start: + if (is_digit(input)) + { + req.http_version_minor = req.http_version_minor * 10 + input - '0'; + state_ = http_version_minor; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_minor: + if (input == '\r') + { + state_ = expecting_newline_1; + return boost::indeterminate; + } + else if (is_digit(input)) + { + req.http_version_minor = req.http_version_minor * 10 + input - '0'; + return boost::indeterminate; + } + else + { + return false; + } + case expecting_newline_1: + if (input == '\n') + { + state_ = header_line_start; + return boost::indeterminate; + } + else + { + return false; + } + case header_line_start: + if (input == '\r') + { + state_ = expecting_newline_3; + return boost::indeterminate; + } + else if (!req.headers.empty() && (input == ' ' || input == '\t')) + { + state_ = header_lws; + return boost::indeterminate; + } + else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + req.headers.push_back(header()); + req.headers.back().name.push_back(input); + state_ = header_name; + return boost::indeterminate; + } + case header_lws: + if (input == '\r') + { + state_ = expecting_newline_2; + return boost::indeterminate; + } + else if (input == ' ' || input == '\t') + { + return boost::indeterminate; + } + else if (is_ctl(input)) + { + return false; + } + else + { + state_ = header_value; + req.headers.back().value.push_back(input); + return boost::indeterminate; + } + case header_name: + if (input == ':') + { + state_ = space_before_header_value; + return boost::indeterminate; + } + else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + req.headers.back().name.push_back(input); + return boost::indeterminate; + } + case space_before_header_value: + if (input == ' ') + { + state_ = header_value; + return boost::indeterminate; + } + else + { + return false; + } + case header_value: + if (input == '\r') + { + state_ = expecting_newline_2; + return boost::indeterminate; + } + else if (is_ctl(input)) + { + return false; + } + else + { + req.headers.back().value.push_back(input); + return boost::indeterminate; + } + case expecting_newline_2: + if (input == '\n') + { + state_ = header_line_start; + return boost::indeterminate; + } + else + { + return false; + } + case expecting_newline_3: + return (input == '\n'); + default: + return false; + } +} + +bool request_parser::is_char(int c) +{ + return c >= 0 && c <= 127; +} + +bool request_parser::is_ctl(int c) +{ + return (c >= 0 && c <= 31) || (c == 127); +} + +bool request_parser::is_tspecial(int c) +{ + switch (c) + { + case '(': case ')': case '<': case '>': case '@': + case ',': case ';': case ':': case '\\': case '"': + case '/': case '[': case ']': case '?': case '=': + case '{': case '}': case ' ': case '\t': + return true; + default: + return false; + } +} + +bool request_parser::is_digit(int c) +{ + return c >= '0' && c <= '9'; +} + +} // namespace server +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server/request_parser.hpp b/3rdparty/asio/src/examples/cpp03/http/server/request_parser.hpp new file mode 100644 index 00000000000..d7da53cf030 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server/request_parser.hpp @@ -0,0 +1,95 @@ +// +// request_parser.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_REQUEST_PARSER_HPP +#define HTTP_REQUEST_PARSER_HPP + +#include <boost/logic/tribool.hpp> +#include <boost/tuple/tuple.hpp> + +namespace http { +namespace server { + +struct request; + +/// Parser for incoming requests. +class request_parser +{ +public: + /// Construct ready to parse the request method. + request_parser(); + + /// Reset to initial parser state. + void reset(); + + /// Parse some data. The tribool return value is true when a complete request + /// has been parsed, false if the data is invalid, indeterminate when more + /// data is required. The InputIterator return value indicates how much of the + /// input has been consumed. + template <typename InputIterator> + boost::tuple<boost::tribool, InputIterator> parse(request& req, + InputIterator begin, InputIterator end) + { + while (begin != end) + { + boost::tribool result = consume(req, *begin++); + if (result || !result) + return boost::make_tuple(result, begin); + } + boost::tribool result = boost::indeterminate; + return boost::make_tuple(result, begin); + } + +private: + /// Handle the next character of input. + boost::tribool consume(request& req, char input); + + /// Check if a byte is an HTTP character. + static bool is_char(int c); + + /// Check if a byte is an HTTP control character. + static bool is_ctl(int c); + + /// Check if a byte is defined as an HTTP tspecial character. + static bool is_tspecial(int c); + + /// Check if a byte is a digit. + static bool is_digit(int c); + + /// The current state of the parser. + enum state + { + method_start, + method, + uri, + http_version_h, + http_version_t_1, + http_version_t_2, + http_version_p, + http_version_slash, + http_version_major_start, + http_version_major, + http_version_minor_start, + http_version_minor, + expecting_newline_1, + header_line_start, + header_lws, + header_name, + space_before_header_value, + header_value, + expecting_newline_2, + expecting_newline_3 + } state_; +}; + +} // namespace server +} // namespace http + +#endif // HTTP_REQUEST_PARSER_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server/server.cpp b/3rdparty/asio/src/examples/cpp03/http/server/server.cpp new file mode 100644 index 00000000000..89e19d08d42 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server/server.cpp @@ -0,0 +1,94 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "server.hpp" +#include <boost/bind.hpp> +#include <signal.h> + +namespace http { +namespace server { + +server::server(const std::string& address, const std::string& port, + const std::string& doc_root) + : io_context_(), + signals_(io_context_), + acceptor_(io_context_), + connection_manager_(), + new_connection_(), + request_handler_(doc_root) +{ + // Register to handle the signals that indicate when the server should exit. + // It is safe to register for the same signal multiple times in a program, + // provided all registration for the specified signal is made through Asio. + signals_.add(SIGINT); + signals_.add(SIGTERM); +#if defined(SIGQUIT) + signals_.add(SIGQUIT); +#endif // defined(SIGQUIT) + signals_.async_wait(boost::bind(&server::handle_stop, this)); + + // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR). + asio::ip::tcp::resolver resolver(io_context_); + asio::ip::tcp::endpoint endpoint = + *resolver.resolve(address, port).begin(); + acceptor_.open(endpoint.protocol()); + acceptor_.set_option(asio::ip::tcp::acceptor::reuse_address(true)); + acceptor_.bind(endpoint); + acceptor_.listen(); + + start_accept(); +} + +void server::run() +{ + // The io_context::run() call will block until all asynchronous operations + // have finished. While the server is running, there is always at least one + // asynchronous operation outstanding: the asynchronous accept call waiting + // for new incoming connections. + io_context_.run(); +} + +void server::start_accept() +{ + new_connection_.reset(new connection(io_context_, + connection_manager_, request_handler_)); + acceptor_.async_accept(new_connection_->socket(), + boost::bind(&server::handle_accept, this, + asio::placeholders::error)); +} + +void server::handle_accept(const asio::error_code& e) +{ + // Check whether the server was stopped by a signal before this completion + // handler had a chance to run. + if (!acceptor_.is_open()) + { + return; + } + + if (!e) + { + connection_manager_.start(new_connection_); + } + + start_accept(); +} + +void server::handle_stop() +{ + // The server is stopped by cancelling all outstanding asynchronous + // operations. Once all operations have finished the io_context::run() call + // will exit. + acceptor_.close(); + connection_manager_.stop_all(); +} + +} // namespace server +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server/server.hpp b/3rdparty/asio/src/examples/cpp03/http/server/server.hpp new file mode 100644 index 00000000000..8b0b1a23296 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server/server.hpp @@ -0,0 +1,69 @@ +// +// server.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER_HPP +#define HTTP_SERVER_HPP + +#include <asio.hpp> +#include <string> +#include <boost/noncopyable.hpp> +#include "connection.hpp" +#include "connection_manager.hpp" +#include "request_handler.hpp" + +namespace http { +namespace server { + +/// The top-level class of the HTTP server. +class server + : private boost::noncopyable +{ +public: + /// Construct the server to listen on the specified TCP address and port, and + /// serve up files from the given directory. + explicit server(const std::string& address, const std::string& port, + const std::string& doc_root); + + /// Run the server's io_context loop. + void run(); + +private: + /// Initiate an asynchronous accept operation. + void start_accept(); + + /// Handle completion of an asynchronous accept operation. + void handle_accept(const asio::error_code& e); + + /// Handle a request to stop the server. + void handle_stop(); + + /// The io_context used to perform asynchronous operations. + asio::io_context io_context_; + + /// The signal_set is used to register for process termination notifications. + asio::signal_set signals_; + + /// Acceptor used to listen for incoming connections. + asio::ip::tcp::acceptor acceptor_; + + /// The connection manager which owns all live connections. + connection_manager connection_manager_; + + /// The next connection to be accepted. + connection_ptr new_connection_; + + /// The handler for all incoming requests. + request_handler request_handler_; +}; + +} // namespace server +} // namespace http + +#endif // HTTP_SERVER_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server2/.gitignore b/3rdparty/asio/src/examples/cpp03/http/server2/.gitignore new file mode 100644 index 00000000000..0882fa6ced3 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server2/.gitignore @@ -0,0 +1,11 @@ +.deps +.dirstamp +*.o +*.obj +*.exe +*_server +*_client +*.ilk +*.manifest +*.pdb +*.tds diff --git a/3rdparty/asio/src/examples/cpp03/http/server2/connection.cpp b/3rdparty/asio/src/examples/cpp03/http/server2/connection.cpp new file mode 100644 index 00000000000..81f4a102217 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server2/connection.cpp @@ -0,0 +1,93 @@ +// +// connection.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "connection.hpp" +#include <vector> +#include <boost/bind.hpp> +#include "request_handler.hpp" + +namespace http { +namespace server2 { + +connection::connection(asio::io_context& io_context, + request_handler& handler) + : socket_(io_context), + request_handler_(handler) +{ +} + +asio::ip::tcp::socket& connection::socket() +{ + return socket_; +} + +void connection::start() +{ + socket_.async_read_some(asio::buffer(buffer_), + boost::bind(&connection::handle_read, shared_from_this(), + asio::placeholders::error, + asio::placeholders::bytes_transferred)); +} + +void connection::handle_read(const asio::error_code& e, + std::size_t bytes_transferred) +{ + if (!e) + { + boost::tribool result; + boost::tie(result, boost::tuples::ignore) = request_parser_.parse( + request_, buffer_.data(), buffer_.data() + bytes_transferred); + + if (result) + { + request_handler_.handle_request(request_, reply_); + asio::async_write(socket_, reply_.to_buffers(), + boost::bind(&connection::handle_write, shared_from_this(), + asio::placeholders::error)); + } + else if (!result) + { + reply_ = reply::stock_reply(reply::bad_request); + asio::async_write(socket_, reply_.to_buffers(), + boost::bind(&connection::handle_write, shared_from_this(), + asio::placeholders::error)); + } + else + { + socket_.async_read_some(asio::buffer(buffer_), + boost::bind(&connection::handle_read, shared_from_this(), + asio::placeholders::error, + asio::placeholders::bytes_transferred)); + } + } + + // If an error occurs then no new asynchronous operations are started. This + // means that all shared_ptr references to the connection object will + // disappear and the object will be destroyed automatically after this + // handler returns. The connection class's destructor closes the socket. +} + +void connection::handle_write(const asio::error_code& e) +{ + if (!e) + { + // Initiate graceful connection closure. + asio::error_code ignored_ec; + socket_.shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec); + } + + // No new asynchronous operations are started. This means that all shared_ptr + // references to the connection object will disappear and the object will be + // destroyed automatically after this handler returns. The connection class's + // destructor closes the socket. +} + +} // namespace server2 +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server2/connection.hpp b/3rdparty/asio/src/examples/cpp03/http/server2/connection.hpp new file mode 100644 index 00000000000..9c624a8a172 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server2/connection.hpp @@ -0,0 +1,75 @@ +// +// connection.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER2_CONNECTION_HPP +#define HTTP_SERVER2_CONNECTION_HPP + +#include <asio.hpp> +#include <boost/array.hpp> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include "reply.hpp" +#include "request.hpp" +#include "request_handler.hpp" +#include "request_parser.hpp" + +namespace http { +namespace server2 { + +/// Represents a single connection from a client. +class connection + : public boost::enable_shared_from_this<connection>, + private boost::noncopyable +{ +public: + /// Construct a connection with the given io_context. + explicit connection(asio::io_context& io_context, + request_handler& handler); + + /// Get the socket associated with the connection. + asio::ip::tcp::socket& socket(); + + /// Start the first asynchronous operation for the connection. + void start(); + +private: + /// Handle completion of a read operation. + void handle_read(const asio::error_code& e, + std::size_t bytes_transferred); + + /// Handle completion of a write operation. + void handle_write(const asio::error_code& e); + + /// Socket for the connection. + asio::ip::tcp::socket socket_; + + /// The handler used to process the incoming request. + request_handler& request_handler_; + + /// Buffer for incoming data. + boost::array<char, 8192> buffer_; + + /// The incoming request. + request request_; + + /// The parser for the incoming request. + request_parser request_parser_; + + /// The reply to be sent back to the client. + reply reply_; +}; + +typedef boost::shared_ptr<connection> connection_ptr; + +} // namespace server2 +} // namespace http + +#endif // HTTP_SERVER2_CONNECTION_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server2/header.hpp b/3rdparty/asio/src/examples/cpp03/http/server2/header.hpp new file mode 100644 index 00000000000..0e44c551409 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server2/header.hpp @@ -0,0 +1,28 @@ +// +// header.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER2_HEADER_HPP +#define HTTP_SERVER2_HEADER_HPP + +#include <string> + +namespace http { +namespace server2 { + +struct header +{ + std::string name; + std::string value; +}; + +} // namespace server2 +} // namespace http + +#endif // HTTP_SERVER2_HEADER_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server2/io_context_pool.cpp b/3rdparty/asio/src/examples/cpp03/http/server2/io_context_pool.cpp new file mode 100644 index 00000000000..049ac988615 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server2/io_context_pool.cpp @@ -0,0 +1,69 @@ +// +// io_context_pool.cpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "server.hpp" +#include <stdexcept> +#include <boost/bind.hpp> +#include <boost/shared_ptr.hpp> + +namespace http { +namespace server2 { + +io_context_pool::io_context_pool(std::size_t pool_size) + : next_io_context_(0) +{ + if (pool_size == 0) + throw std::runtime_error("io_context_pool size is 0"); + + // Give all the io_contexts work to do so that their run() functions will not + // exit until they are explicitly stopped. + for (std::size_t i = 0; i < pool_size; ++i) + { + io_context_ptr io_context(new asio::io_context); + io_contexts_.push_back(io_context); + work_.push_back(asio::make_work_guard(*io_context)); + } +} + +void io_context_pool::run() +{ + // Create a pool of threads to run all of the io_contexts. + std::vector<boost::shared_ptr<asio::thread> > threads; + for (std::size_t i = 0; i < io_contexts_.size(); ++i) + { + boost::shared_ptr<asio::thread> thread(new asio::thread( + boost::bind(&asio::io_context::run, io_contexts_[i]))); + threads.push_back(thread); + } + + // Wait for all threads in the pool to exit. + for (std::size_t i = 0; i < threads.size(); ++i) + threads[i]->join(); +} + +void io_context_pool::stop() +{ + // Explicitly stop all io_contexts. + for (std::size_t i = 0; i < io_contexts_.size(); ++i) + io_contexts_[i]->stop(); +} + +asio::io_context& io_context_pool::get_io_context() +{ + // Use a round-robin scheme to choose the next io_context to use. + asio::io_context& io_context = *io_contexts_[next_io_context_]; + ++next_io_context_; + if (next_io_context_ == io_contexts_.size()) + next_io_context_ = 0; + return io_context; +} + +} // namespace server2 +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server2/io_context_pool.hpp b/3rdparty/asio/src/examples/cpp03/http/server2/io_context_pool.hpp new file mode 100644 index 00000000000..6a573c539f6 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server2/io_context_pool.hpp @@ -0,0 +1,57 @@ +// +// io_context_pool.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER2_IO_SERVICE_POOL_HPP +#define HTTP_SERVER2_IO_SERVICE_POOL_HPP + +#include <asio.hpp> +#include <vector> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> + +namespace http { +namespace server2 { + +/// A pool of io_context objects. +class io_context_pool + : private boost::noncopyable +{ +public: + /// Construct the io_context pool. + explicit io_context_pool(std::size_t pool_size); + + /// Run all io_context objects in the pool. + void run(); + + /// Stop all io_context objects in the pool. + void stop(); + + /// Get an io_context to use. + asio::io_context& get_io_context(); + +private: + typedef boost::shared_ptr<asio::io_context> io_context_ptr; + typedef asio::executor_work_guard< + asio::io_context::executor_type> io_context_work; + + /// The pool of io_contexts. + std::vector<io_context_ptr> io_contexts_; + + /// The work that keeps the io_contexts running. + std::vector<io_context_work> work_; + + /// The next io_context to use for a connection. + std::size_t next_io_context_; +}; + +} // namespace server2 +} // namespace http + +#endif // HTTP_SERVER2_IO_SERVICE_POOL_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server2/main.cpp b/3rdparty/asio/src/examples/cpp03/http/server2/main.cpp new file mode 100644 index 00000000000..2d224c5bac1 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server2/main.cpp @@ -0,0 +1,46 @@ +// +// main.cpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <iostream> +#include <string> +#include <asio.hpp> +#include <boost/bind.hpp> +#include <boost/lexical_cast.hpp> +#include "server.hpp" + +int main(int argc, char* argv[]) +{ + try + { + // Check command line arguments. + if (argc != 5) + { + std::cerr << "Usage: http_server <address> <port> <threads> <doc_root>\n"; + std::cerr << " For IPv4, try:\n"; + std::cerr << " receiver 0.0.0.0 80 1 .\n"; + std::cerr << " For IPv6, try:\n"; + std::cerr << " receiver 0::0 80 1 .\n"; + return 1; + } + + // Initialise the server. + std::size_t num_threads = boost::lexical_cast<std::size_t>(argv[3]); + http::server2::server s(argv[1], argv[2], argv[4], num_threads); + + // Run the server until stopped. + s.run(); + } + catch (std::exception& e) + { + std::cerr << "exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/3rdparty/asio/src/examples/cpp03/http/server2/mime_types.cpp b/3rdparty/asio/src/examples/cpp03/http/server2/mime_types.cpp new file mode 100644 index 00000000000..640f3aedc44 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server2/mime_types.cpp @@ -0,0 +1,46 @@ +// +// mime_types.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "mime_types.hpp" + +namespace http { +namespace server2 { +namespace mime_types { + +struct mapping +{ + const char* extension; + const char* mime_type; +} mappings[] = +{ + { "gif", "image/gif" }, + { "htm", "text/html" }, + { "html", "text/html" }, + { "jpg", "image/jpeg" }, + { "png", "image/png" }, + { 0, 0 } // Marks end of list. +}; + +std::string extension_to_type(const std::string& extension) +{ + for (mapping* m = mappings; m->extension; ++m) + { + if (m->extension == extension) + { + return m->mime_type; + } + } + + return "text/plain"; +} + +} // namespace mime_types +} // namespace server2 +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server2/mime_types.hpp b/3rdparty/asio/src/examples/cpp03/http/server2/mime_types.hpp new file mode 100644 index 00000000000..57d1412b7b1 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server2/mime_types.hpp @@ -0,0 +1,27 @@ +// +// mime_types.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER2_MIME_TYPES_HPP +#define HTTP_SERVER2_MIME_TYPES_HPP + +#include <string> + +namespace http { +namespace server2 { +namespace mime_types { + +/// Convert a file extension into a MIME type. +std::string extension_to_type(const std::string& extension); + +} // namespace mime_types +} // namespace server2 +} // namespace http + +#endif // HTTP_SERVER2_MIME_TYPES_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server2/reply.cpp b/3rdparty/asio/src/examples/cpp03/http/server2/reply.cpp new file mode 100644 index 00000000000..958577b2ce4 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server2/reply.cpp @@ -0,0 +1,256 @@ +// +// reply.cpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "reply.hpp" +#include <string> +#include <boost/lexical_cast.hpp> + +namespace http { +namespace server2 { + +namespace status_strings { + +const std::string ok = + "HTTP/1.0 200 OK\r\n"; +const std::string created = + "HTTP/1.0 201 Created\r\n"; +const std::string accepted = + "HTTP/1.0 202 Accepted\r\n"; +const std::string no_content = + "HTTP/1.0 204 No Content\r\n"; +const std::string multiple_choices = + "HTTP/1.0 300 Multiple Choices\r\n"; +const std::string moved_permanently = + "HTTP/1.0 301 Moved Permanently\r\n"; +const std::string moved_temporarily = + "HTTP/1.0 302 Moved Temporarily\r\n"; +const std::string not_modified = + "HTTP/1.0 304 Not Modified\r\n"; +const std::string bad_request = + "HTTP/1.0 400 Bad Request\r\n"; +const std::string unauthorized = + "HTTP/1.0 401 Unauthorized\r\n"; +const std::string forbidden = + "HTTP/1.0 403 Forbidden\r\n"; +const std::string not_found = + "HTTP/1.0 404 Not Found\r\n"; +const std::string internal_server_error = + "HTTP/1.0 500 Internal Server Error\r\n"; +const std::string not_implemented = + "HTTP/1.0 501 Not Implemented\r\n"; +const std::string bad_gateway = + "HTTP/1.0 502 Bad Gateway\r\n"; +const std::string service_unavailable = + "HTTP/1.0 503 Service Unavailable\r\n"; + +asio::const_buffer to_buffer(reply::status_type status) +{ + switch (status) + { + case reply::ok: + return asio::buffer(ok); + case reply::created: + return asio::buffer(created); + case reply::accepted: + return asio::buffer(accepted); + case reply::no_content: + return asio::buffer(no_content); + case reply::multiple_choices: + return asio::buffer(multiple_choices); + case reply::moved_permanently: + return asio::buffer(moved_permanently); + case reply::moved_temporarily: + return asio::buffer(moved_temporarily); + case reply::not_modified: + return asio::buffer(not_modified); + case reply::bad_request: + return asio::buffer(bad_request); + case reply::unauthorized: + return asio::buffer(unauthorized); + case reply::forbidden: + return asio::buffer(forbidden); + case reply::not_found: + return asio::buffer(not_found); + case reply::internal_server_error: + return asio::buffer(internal_server_error); + case reply::not_implemented: + return asio::buffer(not_implemented); + case reply::bad_gateway: + return asio::buffer(bad_gateway); + case reply::service_unavailable: + return asio::buffer(service_unavailable); + default: + return asio::buffer(internal_server_error); + } +} + +} // namespace status_strings + +namespace misc_strings { + +const char name_value_separator[] = { ':', ' ' }; +const char crlf[] = { '\r', '\n' }; + +} // namespace misc_strings + +std::vector<asio::const_buffer> reply::to_buffers() +{ + std::vector<asio::const_buffer> buffers; + buffers.push_back(status_strings::to_buffer(status)); + for (std::size_t i = 0; i < headers.size(); ++i) + { + header& h = headers[i]; + buffers.push_back(asio::buffer(h.name)); + buffers.push_back(asio::buffer(misc_strings::name_value_separator)); + buffers.push_back(asio::buffer(h.value)); + buffers.push_back(asio::buffer(misc_strings::crlf)); + } + buffers.push_back(asio::buffer(misc_strings::crlf)); + buffers.push_back(asio::buffer(content)); + return buffers; +} + +namespace stock_replies { + +const char ok[] = ""; +const char created[] = + "<html>" + "<head><title>Created</title></head>" + "<body><h1>201 Created</h1></body>" + "</html>"; +const char accepted[] = + "<html>" + "<head><title>Accepted</title></head>" + "<body><h1>202 Accepted</h1></body>" + "</html>"; +const char no_content[] = + "<html>" + "<head><title>No Content</title></head>" + "<body><h1>204 Content</h1></body>" + "</html>"; +const char multiple_choices[] = + "<html>" + "<head><title>Multiple Choices</title></head>" + "<body><h1>300 Multiple Choices</h1></body>" + "</html>"; +const char moved_permanently[] = + "<html>" + "<head><title>Moved Permanently</title></head>" + "<body><h1>301 Moved Permanently</h1></body>" + "</html>"; +const char moved_temporarily[] = + "<html>" + "<head><title>Moved Temporarily</title></head>" + "<body><h1>302 Moved Temporarily</h1></body>" + "</html>"; +const char not_modified[] = + "<html>" + "<head><title>Not Modified</title></head>" + "<body><h1>304 Not Modified</h1></body>" + "</html>"; +const char bad_request[] = + "<html>" + "<head><title>Bad Request</title></head>" + "<body><h1>400 Bad Request</h1></body>" + "</html>"; +const char unauthorized[] = + "<html>" + "<head><title>Unauthorized</title></head>" + "<body><h1>401 Unauthorized</h1></body>" + "</html>"; +const char forbidden[] = + "<html>" + "<head><title>Forbidden</title></head>" + "<body><h1>403 Forbidden</h1></body>" + "</html>"; +const char not_found[] = + "<html>" + "<head><title>Not Found</title></head>" + "<body><h1>404 Not Found</h1></body>" + "</html>"; +const char internal_server_error[] = + "<html>" + "<head><title>Internal Server Error</title></head>" + "<body><h1>500 Internal Server Error</h1></body>" + "</html>"; +const char not_implemented[] = + "<html>" + "<head><title>Not Implemented</title></head>" + "<body><h1>501 Not Implemented</h1></body>" + "</html>"; +const char bad_gateway[] = + "<html>" + "<head><title>Bad Gateway</title></head>" + "<body><h1>502 Bad Gateway</h1></body>" + "</html>"; +const char service_unavailable[] = + "<html>" + "<head><title>Service Unavailable</title></head>" + "<body><h1>503 Service Unavailable</h1></body>" + "</html>"; + +std::string to_string(reply::status_type status) +{ + switch (status) + { + case reply::ok: + return ok; + case reply::created: + return created; + case reply::accepted: + return accepted; + case reply::no_content: + return no_content; + case reply::multiple_choices: + return multiple_choices; + case reply::moved_permanently: + return moved_permanently; + case reply::moved_temporarily: + return moved_temporarily; + case reply::not_modified: + return not_modified; + case reply::bad_request: + return bad_request; + case reply::unauthorized: + return unauthorized; + case reply::forbidden: + return forbidden; + case reply::not_found: + return not_found; + case reply::internal_server_error: + return internal_server_error; + case reply::not_implemented: + return not_implemented; + case reply::bad_gateway: + return bad_gateway; + case reply::service_unavailable: + return service_unavailable; + default: + return internal_server_error; + } +} + +} // namespace stock_replies + +reply reply::stock_reply(reply::status_type status) +{ + reply rep; + rep.status = status; + rep.content = stock_replies::to_string(status); + rep.headers.resize(2); + rep.headers[0].name = "Content-Length"; + rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size()); + rep.headers[1].name = "Content-Type"; + rep.headers[1].value = "text/html"; + return rep; +} + +} // namespace server2 +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server2/reply.hpp b/3rdparty/asio/src/examples/cpp03/http/server2/reply.hpp new file mode 100644 index 00000000000..17f11e7dde3 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server2/reply.hpp @@ -0,0 +1,64 @@ +// +// reply.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER2_REPLY_HPP +#define HTTP_SERVER2_REPLY_HPP + +#include <string> +#include <vector> +#include <asio.hpp> +#include "header.hpp" + +namespace http { +namespace server2 { + +/// A reply to be sent to a client. +struct reply +{ + /// The status of the reply. + enum status_type + { + ok = 200, + created = 201, + accepted = 202, + no_content = 204, + multiple_choices = 300, + moved_permanently = 301, + moved_temporarily = 302, + not_modified = 304, + bad_request = 400, + unauthorized = 401, + forbidden = 403, + not_found = 404, + internal_server_error = 500, + not_implemented = 501, + bad_gateway = 502, + service_unavailable = 503 + } status; + + /// The headers to be included in the reply. + std::vector<header> headers; + + /// The content to be sent in the reply. + std::string content; + + /// Convert the reply into a vector of buffers. The buffers do not own the + /// underlying memory blocks, therefore the reply object must remain valid and + /// not be changed until the write operation has completed. + std::vector<asio::const_buffer> to_buffers(); + + /// Get a stock reply. + static reply stock_reply(status_type status); +}; + +} // namespace server2 +} // namespace http + +#endif // HTTP_SERVER2_REPLY_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server2/request.hpp b/3rdparty/asio/src/examples/cpp03/http/server2/request.hpp new file mode 100644 index 00000000000..39070ee2f84 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server2/request.hpp @@ -0,0 +1,34 @@ +// +// request.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER2_REQUEST_HPP +#define HTTP_SERVER2_REQUEST_HPP + +#include <string> +#include <vector> +#include "header.hpp" + +namespace http { +namespace server2 { + +/// A request received from a client. +struct request +{ + std::string method; + std::string uri; + int http_version_major; + int http_version_minor; + std::vector<header> headers; +}; + +} // namespace server2 +} // namespace http + +#endif // HTTP_SERVER2_REQUEST_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server2/request_handler.cpp b/3rdparty/asio/src/examples/cpp03/http/server2/request_handler.cpp new file mode 100644 index 00000000000..9369c107166 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server2/request_handler.cpp @@ -0,0 +1,122 @@ +// +// request_handler.cpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "request_handler.hpp" +#include <fstream> +#include <sstream> +#include <string> +#include <boost/lexical_cast.hpp> +#include "mime_types.hpp" +#include "reply.hpp" +#include "request.hpp" + +namespace http { +namespace server2 { + +request_handler::request_handler(const std::string& doc_root) + : doc_root_(doc_root) +{ +} + +void request_handler::handle_request(const request& req, reply& rep) +{ + // Decode url to path. + std::string request_path; + if (!url_decode(req.uri, request_path)) + { + rep = reply::stock_reply(reply::bad_request); + return; + } + + // Request path must be absolute and not contain "..". + if (request_path.empty() || request_path[0] != '/' + || request_path.find("..") != std::string::npos) + { + rep = reply::stock_reply(reply::bad_request); + return; + } + + // If path ends in slash (i.e. is a directory) then add "index.html". + if (request_path[request_path.size() - 1] == '/') + { + request_path += "index.html"; + } + + // Determine the file extension. + std::size_t last_slash_pos = request_path.find_last_of("/"); + std::size_t last_dot_pos = request_path.find_last_of("."); + std::string extension; + if (last_dot_pos != std::string::npos && last_dot_pos > last_slash_pos) + { + extension = request_path.substr(last_dot_pos + 1); + } + + // Open the file to send back. + std::string full_path = doc_root_ + request_path; + std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary); + if (!is) + { + rep = reply::stock_reply(reply::not_found); + return; + } + + // Fill out the reply to be sent to the client. + rep.status = reply::ok; + char buf[512]; + while (is.read(buf, sizeof(buf)).gcount() > 0) + rep.content.append(buf, is.gcount()); + rep.headers.resize(2); + rep.headers[0].name = "Content-Length"; + rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size()); + rep.headers[1].name = "Content-Type"; + rep.headers[1].value = mime_types::extension_to_type(extension); +} + +bool request_handler::url_decode(const std::string& in, std::string& out) +{ + out.clear(); + out.reserve(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) + { + if (in[i] == '%') + { + if (i + 3 <= in.size()) + { + int value = 0; + std::istringstream is(in.substr(i + 1, 2)); + if (is >> std::hex >> value) + { + out += static_cast<char>(value); + i += 2; + } + else + { + return false; + } + } + else + { + return false; + } + } + else if (in[i] == '+') + { + out += ' '; + } + else + { + out += in[i]; + } + } + return true; +} + +} // namespace server2 +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server2/request_handler.hpp b/3rdparty/asio/src/examples/cpp03/http/server2/request_handler.hpp new file mode 100644 index 00000000000..8d24686fa54 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server2/request_handler.hpp @@ -0,0 +1,46 @@ +// +// request_handler.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER2_REQUEST_HANDLER_HPP +#define HTTP_SERVER2_REQUEST_HANDLER_HPP + +#include <string> +#include <boost/noncopyable.hpp> + +namespace http { +namespace server2 { + +struct reply; +struct request; + +/// The common handler for all incoming requests. +class request_handler + : private boost::noncopyable +{ +public: + /// Construct with a directory containing files to be served. + explicit request_handler(const std::string& doc_root); + + /// Handle a request and produce a reply. + void handle_request(const request& req, reply& rep); + +private: + /// The directory containing the files to be served. + std::string doc_root_; + + /// Perform URL-decoding on a string. Returns false if the encoding was + /// invalid. + static bool url_decode(const std::string& in, std::string& out); +}; + +} // namespace server2 +} // namespace http + +#endif // HTTP_SERVER2_REQUEST_HANDLER_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server2/request_parser.cpp b/3rdparty/asio/src/examples/cpp03/http/server2/request_parser.cpp new file mode 100644 index 00000000000..ae80b20f784 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server2/request_parser.cpp @@ -0,0 +1,315 @@ +// +// request_parser.cpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "request_parser.hpp" +#include "request.hpp" + +namespace http { +namespace server2 { + +request_parser::request_parser() + : state_(method_start) +{ +} + +void request_parser::reset() +{ + state_ = method_start; +} + +boost::tribool request_parser::consume(request& req, char input) +{ + switch (state_) + { + case method_start: + if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + state_ = method; + req.method.push_back(input); + return boost::indeterminate; + } + case method: + if (input == ' ') + { + state_ = uri; + return boost::indeterminate; + } + else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + req.method.push_back(input); + return boost::indeterminate; + } + case uri: + if (input == ' ') + { + state_ = http_version_h; + return boost::indeterminate; + } + else if (is_ctl(input)) + { + return false; + } + else + { + req.uri.push_back(input); + return boost::indeterminate; + } + case http_version_h: + if (input == 'H') + { + state_ = http_version_t_1; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_t_1: + if (input == 'T') + { + state_ = http_version_t_2; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_t_2: + if (input == 'T') + { + state_ = http_version_p; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_p: + if (input == 'P') + { + state_ = http_version_slash; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_slash: + if (input == '/') + { + req.http_version_major = 0; + req.http_version_minor = 0; + state_ = http_version_major_start; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_major_start: + if (is_digit(input)) + { + req.http_version_major = req.http_version_major * 10 + input - '0'; + state_ = http_version_major; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_major: + if (input == '.') + { + state_ = http_version_minor_start; + return boost::indeterminate; + } + else if (is_digit(input)) + { + req.http_version_major = req.http_version_major * 10 + input - '0'; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_minor_start: + if (is_digit(input)) + { + req.http_version_minor = req.http_version_minor * 10 + input - '0'; + state_ = http_version_minor; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_minor: + if (input == '\r') + { + state_ = expecting_newline_1; + return boost::indeterminate; + } + else if (is_digit(input)) + { + req.http_version_minor = req.http_version_minor * 10 + input - '0'; + return boost::indeterminate; + } + else + { + return false; + } + case expecting_newline_1: + if (input == '\n') + { + state_ = header_line_start; + return boost::indeterminate; + } + else + { + return false; + } + case header_line_start: + if (input == '\r') + { + state_ = expecting_newline_3; + return boost::indeterminate; + } + else if (!req.headers.empty() && (input == ' ' || input == '\t')) + { + state_ = header_lws; + return boost::indeterminate; + } + else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + req.headers.push_back(header()); + req.headers.back().name.push_back(input); + state_ = header_name; + return boost::indeterminate; + } + case header_lws: + if (input == '\r') + { + state_ = expecting_newline_2; + return boost::indeterminate; + } + else if (input == ' ' || input == '\t') + { + return boost::indeterminate; + } + else if (is_ctl(input)) + { + return false; + } + else + { + state_ = header_value; + req.headers.back().value.push_back(input); + return boost::indeterminate; + } + case header_name: + if (input == ':') + { + state_ = space_before_header_value; + return boost::indeterminate; + } + else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + req.headers.back().name.push_back(input); + return boost::indeterminate; + } + case space_before_header_value: + if (input == ' ') + { + state_ = header_value; + return boost::indeterminate; + } + else + { + return false; + } + case header_value: + if (input == '\r') + { + state_ = expecting_newline_2; + return boost::indeterminate; + } + else if (is_ctl(input)) + { + return false; + } + else + { + req.headers.back().value.push_back(input); + return boost::indeterminate; + } + case expecting_newline_2: + if (input == '\n') + { + state_ = header_line_start; + return boost::indeterminate; + } + else + { + return false; + } + case expecting_newline_3: + return (input == '\n'); + default: + return false; + } +} + +bool request_parser::is_char(int c) +{ + return c >= 0 && c <= 127; +} + +bool request_parser::is_ctl(int c) +{ + return (c >= 0 && c <= 31) || (c == 127); +} + +bool request_parser::is_tspecial(int c) +{ + switch (c) + { + case '(': case ')': case '<': case '>': case '@': + case ',': case ';': case ':': case '\\': case '"': + case '/': case '[': case ']': case '?': case '=': + case '{': case '}': case ' ': case '\t': + return true; + default: + return false; + } +} + +bool request_parser::is_digit(int c) +{ + return c >= '0' && c <= '9'; +} + +} // namespace server2 +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server2/request_parser.hpp b/3rdparty/asio/src/examples/cpp03/http/server2/request_parser.hpp new file mode 100644 index 00000000000..133a6492058 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server2/request_parser.hpp @@ -0,0 +1,95 @@ +// +// request_parser.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER2_REQUEST_PARSER_HPP +#define HTTP_SERVER2_REQUEST_PARSER_HPP + +#include <boost/logic/tribool.hpp> +#include <boost/tuple/tuple.hpp> + +namespace http { +namespace server2 { + +struct request; + +/// Parser for incoming requests. +class request_parser +{ +public: + /// Construct ready to parse the request method. + request_parser(); + + /// Reset to initial parser state. + void reset(); + + /// Parse some data. The tribool return value is true when a complete request + /// has been parsed, false if the data is invalid, indeterminate when more + /// data is required. The InputIterator return value indicates how much of the + /// input has been consumed. + template <typename InputIterator> + boost::tuple<boost::tribool, InputIterator> parse(request& req, + InputIterator begin, InputIterator end) + { + while (begin != end) + { + boost::tribool result = consume(req, *begin++); + if (result || !result) + return boost::make_tuple(result, begin); + } + boost::tribool result = boost::indeterminate; + return boost::make_tuple(result, begin); + } + +private: + /// Handle the next character of input. + boost::tribool consume(request& req, char input); + + /// Check if a byte is an HTTP character. + static bool is_char(int c); + + /// Check if a byte is an HTTP control character. + static bool is_ctl(int c); + + /// Check if a byte is defined as an HTTP tspecial character. + static bool is_tspecial(int c); + + /// Check if a byte is a digit. + static bool is_digit(int c); + + /// The current state of the parser. + enum state + { + method_start, + method, + uri, + http_version_h, + http_version_t_1, + http_version_t_2, + http_version_p, + http_version_slash, + http_version_major_start, + http_version_major, + http_version_minor_start, + http_version_minor, + expecting_newline_1, + header_line_start, + header_lws, + header_name, + space_before_header_value, + header_value, + expecting_newline_2, + expecting_newline_3 + } state_; +}; + +} // namespace server2 +} // namespace http + +#endif // HTTP_SERVER2_REQUEST_PARSER_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server2/server.cpp b/3rdparty/asio/src/examples/cpp03/http/server2/server.cpp new file mode 100644 index 00000000000..75d3a48aa84 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server2/server.cpp @@ -0,0 +1,77 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "server.hpp" +#include <boost/bind.hpp> + +namespace http { +namespace server2 { + +server::server(const std::string& address, const std::string& port, + const std::string& doc_root, std::size_t io_context_pool_size) + : io_context_pool_(io_context_pool_size), + signals_(io_context_pool_.get_io_context()), + acceptor_(io_context_pool_.get_io_context()), + new_connection_(), + request_handler_(doc_root) +{ + // Register to handle the signals that indicate when the server should exit. + // It is safe to register for the same signal multiple times in a program, + // provided all registration for the specified signal is made through Asio. + signals_.add(SIGINT); + signals_.add(SIGTERM); +#if defined(SIGQUIT) + signals_.add(SIGQUIT); +#endif // defined(SIGQUIT) + signals_.async_wait(boost::bind(&server::handle_stop, this)); + + // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR). + asio::ip::tcp::resolver resolver(acceptor_.get_executor().context()); + asio::ip::tcp::endpoint endpoint = + *resolver.resolve(address, port).begin(); + acceptor_.open(endpoint.protocol()); + acceptor_.set_option(asio::ip::tcp::acceptor::reuse_address(true)); + acceptor_.bind(endpoint); + acceptor_.listen(); + + start_accept(); +} + +void server::run() +{ + io_context_pool_.run(); +} + +void server::start_accept() +{ + new_connection_.reset(new connection( + io_context_pool_.get_io_context(), request_handler_)); + acceptor_.async_accept(new_connection_->socket(), + boost::bind(&server::handle_accept, this, + asio::placeholders::error)); +} + +void server::handle_accept(const asio::error_code& e) +{ + if (!e) + { + new_connection_->start(); + } + + start_accept(); +} + +void server::handle_stop() +{ + io_context_pool_.stop(); +} + +} // namespace server2 +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server2/server.hpp b/3rdparty/asio/src/examples/cpp03/http/server2/server.hpp new file mode 100644 index 00000000000..6a742a8220a --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server2/server.hpp @@ -0,0 +1,68 @@ +// +// server.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER2_SERVER_HPP +#define HTTP_SERVER2_SERVER_HPP + +#include <asio.hpp> +#include <string> +#include <vector> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include "connection.hpp" +#include "io_context_pool.hpp" +#include "request_handler.hpp" + +namespace http { +namespace server2 { + +/// The top-level class of the HTTP server. +class server + : private boost::noncopyable +{ +public: + /// Construct the server to listen on the specified TCP address and port, and + /// serve up files from the given directory. + explicit server(const std::string& address, const std::string& port, + const std::string& doc_root, std::size_t io_context_pool_size); + + /// Run the server's io_context loop. + void run(); + +private: + /// Initiate an asynchronous accept operation. + void start_accept(); + + /// Handle completion of an asynchronous accept operation. + void handle_accept(const asio::error_code& e); + + /// Handle a request to stop the server. + void handle_stop(); + + /// The pool of io_context objects used to perform asynchronous operations. + io_context_pool io_context_pool_; + + /// The signal_set is used to register for process termination notifications. + asio::signal_set signals_; + + /// Acceptor used to listen for incoming connections. + asio::ip::tcp::acceptor acceptor_; + + /// The next connection to be accepted. + connection_ptr new_connection_; + + /// The handler for all incoming requests. + request_handler request_handler_; +}; + +} // namespace server2 +} // namespace http + +#endif // HTTP_SERVER2_SERVER_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server3/.gitignore b/3rdparty/asio/src/examples/cpp03/http/server3/.gitignore new file mode 100644 index 00000000000..0882fa6ced3 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server3/.gitignore @@ -0,0 +1,11 @@ +.deps +.dirstamp +*.o +*.obj +*.exe +*_server +*_client +*.ilk +*.manifest +*.pdb +*.tds diff --git a/3rdparty/asio/src/examples/cpp03/http/server3/connection.cpp b/3rdparty/asio/src/examples/cpp03/http/server3/connection.cpp new file mode 100644 index 00000000000..5db960a9109 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server3/connection.cpp @@ -0,0 +1,98 @@ +// +// connection.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "connection.hpp" +#include <vector> +#include <boost/bind.hpp> +#include "request_handler.hpp" + +namespace http { +namespace server3 { + +connection::connection(asio::io_context& io_context, + request_handler& handler) + : strand_(io_context), + socket_(io_context), + request_handler_(handler) +{ +} + +asio::ip::tcp::socket& connection::socket() +{ + return socket_; +} + +void connection::start() +{ + socket_.async_read_some(asio::buffer(buffer_), + asio::bind_executor(strand_, + boost::bind(&connection::handle_read, shared_from_this(), + asio::placeholders::error, + asio::placeholders::bytes_transferred))); +} + +void connection::handle_read(const asio::error_code& e, + std::size_t bytes_transferred) +{ + if (!e) + { + boost::tribool result; + boost::tie(result, boost::tuples::ignore) = request_parser_.parse( + request_, buffer_.data(), buffer_.data() + bytes_transferred); + + if (result) + { + request_handler_.handle_request(request_, reply_); + asio::async_write(socket_, reply_.to_buffers(), + asio::bind_executor(strand_, + boost::bind(&connection::handle_write, shared_from_this(), + asio::placeholders::error))); + } + else if (!result) + { + reply_ = reply::stock_reply(reply::bad_request); + asio::async_write(socket_, reply_.to_buffers(), + asio::bind_executor(strand_, + boost::bind(&connection::handle_write, shared_from_this(), + asio::placeholders::error))); + } + else + { + socket_.async_read_some(asio::buffer(buffer_), + asio::bind_executor(strand_, + boost::bind(&connection::handle_read, shared_from_this(), + asio::placeholders::error, + asio::placeholders::bytes_transferred))); + } + } + + // If an error occurs then no new asynchronous operations are started. This + // means that all shared_ptr references to the connection object will + // disappear and the object will be destroyed automatically after this + // handler returns. The connection class's destructor closes the socket. +} + +void connection::handle_write(const asio::error_code& e) +{ + if (!e) + { + // Initiate graceful connection closure. + asio::error_code ignored_ec; + socket_.shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec); + } + + // No new asynchronous operations are started. This means that all shared_ptr + // references to the connection object will disappear and the object will be + // destroyed automatically after this handler returns. The connection class's + // destructor closes the socket. +} + +} // namespace server3 +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server3/connection.hpp b/3rdparty/asio/src/examples/cpp03/http/server3/connection.hpp new file mode 100644 index 00000000000..2aac1a5c7ae --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server3/connection.hpp @@ -0,0 +1,78 @@ +// +// connection.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER3_CONNECTION_HPP +#define HTTP_SERVER3_CONNECTION_HPP + +#include <asio.hpp> +#include <boost/array.hpp> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include "reply.hpp" +#include "request.hpp" +#include "request_handler.hpp" +#include "request_parser.hpp" + +namespace http { +namespace server3 { + +/// Represents a single connection from a client. +class connection + : public boost::enable_shared_from_this<connection>, + private boost::noncopyable +{ +public: + /// Construct a connection with the given io_context. + explicit connection(asio::io_context& io_context, + request_handler& handler); + + /// Get the socket associated with the connection. + asio::ip::tcp::socket& socket(); + + /// Start the first asynchronous operation for the connection. + void start(); + +private: + /// Handle completion of a read operation. + void handle_read(const asio::error_code& e, + std::size_t bytes_transferred); + + /// Handle completion of a write operation. + void handle_write(const asio::error_code& e); + + /// Strand to ensure the connection's handlers are not called concurrently. + asio::io_context::strand strand_; + + /// Socket for the connection. + asio::ip::tcp::socket socket_; + + /// The handler used to process the incoming request. + request_handler& request_handler_; + + /// Buffer for incoming data. + boost::array<char, 8192> buffer_; + + /// The incoming request. + request request_; + + /// The parser for the incoming request. + request_parser request_parser_; + + /// The reply to be sent back to the client. + reply reply_; +}; + +typedef boost::shared_ptr<connection> connection_ptr; + +} // namespace server3 +} // namespace http + +#endif // HTTP_SERVER3_CONNECTION_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server3/header.hpp b/3rdparty/asio/src/examples/cpp03/http/server3/header.hpp new file mode 100644 index 00000000000..207c6290784 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server3/header.hpp @@ -0,0 +1,28 @@ +// +// header.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER3_HEADER_HPP +#define HTTP_SERVER3_HEADER_HPP + +#include <string> + +namespace http { +namespace server3 { + +struct header +{ + std::string name; + std::string value; +}; + +} // namespace server3 +} // namespace http + +#endif // HTTP_SERVER3_HEADER_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server3/main.cpp b/3rdparty/asio/src/examples/cpp03/http/server3/main.cpp new file mode 100644 index 00000000000..367efad729f --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server3/main.cpp @@ -0,0 +1,46 @@ +// +// main.cpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <iostream> +#include <string> +#include <asio.hpp> +#include <boost/bind.hpp> +#include <boost/lexical_cast.hpp> +#include "server.hpp" + +int main(int argc, char* argv[]) +{ + try + { + // Check command line arguments. + if (argc != 5) + { + std::cerr << "Usage: http_server <address> <port> <threads> <doc_root>\n"; + std::cerr << " For IPv4, try:\n"; + std::cerr << " receiver 0.0.0.0 80 1 .\n"; + std::cerr << " For IPv6, try:\n"; + std::cerr << " receiver 0::0 80 1 .\n"; + return 1; + } + + // Initialise the server. + std::size_t num_threads = boost::lexical_cast<std::size_t>(argv[3]); + http::server3::server s(argv[1], argv[2], argv[4], num_threads); + + // Run the server until stopped. + s.run(); + } + catch (std::exception& e) + { + std::cerr << "exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/3rdparty/asio/src/examples/cpp03/http/server3/mime_types.cpp b/3rdparty/asio/src/examples/cpp03/http/server3/mime_types.cpp new file mode 100644 index 00000000000..0b0c66e3ce4 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server3/mime_types.cpp @@ -0,0 +1,46 @@ +// +// mime_types.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "mime_types.hpp" + +namespace http { +namespace server3 { +namespace mime_types { + +struct mapping +{ + const char* extension; + const char* mime_type; +} mappings[] = +{ + { "gif", "image/gif" }, + { "htm", "text/html" }, + { "html", "text/html" }, + { "jpg", "image/jpeg" }, + { "png", "image/png" }, + { 0, 0 } // Marks end of list. +}; + +std::string extension_to_type(const std::string& extension) +{ + for (mapping* m = mappings; m->extension; ++m) + { + if (m->extension == extension) + { + return m->mime_type; + } + } + + return "text/plain"; +} + +} // namespace mime_types +} // namespace server3 +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server3/mime_types.hpp b/3rdparty/asio/src/examples/cpp03/http/server3/mime_types.hpp new file mode 100644 index 00000000000..502766aa5ab --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server3/mime_types.hpp @@ -0,0 +1,27 @@ +// +// mime_types.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER3_MIME_TYPES_HPP +#define HTTP_SERVER3_MIME_TYPES_HPP + +#include <string> + +namespace http { +namespace server3 { +namespace mime_types { + +/// Convert a file extension into a MIME type. +std::string extension_to_type(const std::string& extension); + +} // namespace mime_types +} // namespace server3 +} // namespace http + +#endif // HTTP_SERVER3_MIME_TYPES_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server3/reply.cpp b/3rdparty/asio/src/examples/cpp03/http/server3/reply.cpp new file mode 100644 index 00000000000..79790a1df77 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server3/reply.cpp @@ -0,0 +1,256 @@ +// +// reply.cpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "reply.hpp" +#include <string> +#include <boost/lexical_cast.hpp> + +namespace http { +namespace server3 { + +namespace status_strings { + +const std::string ok = + "HTTP/1.0 200 OK\r\n"; +const std::string created = + "HTTP/1.0 201 Created\r\n"; +const std::string accepted = + "HTTP/1.0 202 Accepted\r\n"; +const std::string no_content = + "HTTP/1.0 204 No Content\r\n"; +const std::string multiple_choices = + "HTTP/1.0 300 Multiple Choices\r\n"; +const std::string moved_permanently = + "HTTP/1.0 301 Moved Permanently\r\n"; +const std::string moved_temporarily = + "HTTP/1.0 302 Moved Temporarily\r\n"; +const std::string not_modified = + "HTTP/1.0 304 Not Modified\r\n"; +const std::string bad_request = + "HTTP/1.0 400 Bad Request\r\n"; +const std::string unauthorized = + "HTTP/1.0 401 Unauthorized\r\n"; +const std::string forbidden = + "HTTP/1.0 403 Forbidden\r\n"; +const std::string not_found = + "HTTP/1.0 404 Not Found\r\n"; +const std::string internal_server_error = + "HTTP/1.0 500 Internal Server Error\r\n"; +const std::string not_implemented = + "HTTP/1.0 501 Not Implemented\r\n"; +const std::string bad_gateway = + "HTTP/1.0 502 Bad Gateway\r\n"; +const std::string service_unavailable = + "HTTP/1.0 503 Service Unavailable\r\n"; + +asio::const_buffer to_buffer(reply::status_type status) +{ + switch (status) + { + case reply::ok: + return asio::buffer(ok); + case reply::created: + return asio::buffer(created); + case reply::accepted: + return asio::buffer(accepted); + case reply::no_content: + return asio::buffer(no_content); + case reply::multiple_choices: + return asio::buffer(multiple_choices); + case reply::moved_permanently: + return asio::buffer(moved_permanently); + case reply::moved_temporarily: + return asio::buffer(moved_temporarily); + case reply::not_modified: + return asio::buffer(not_modified); + case reply::bad_request: + return asio::buffer(bad_request); + case reply::unauthorized: + return asio::buffer(unauthorized); + case reply::forbidden: + return asio::buffer(forbidden); + case reply::not_found: + return asio::buffer(not_found); + case reply::internal_server_error: + return asio::buffer(internal_server_error); + case reply::not_implemented: + return asio::buffer(not_implemented); + case reply::bad_gateway: + return asio::buffer(bad_gateway); + case reply::service_unavailable: + return asio::buffer(service_unavailable); + default: + return asio::buffer(internal_server_error); + } +} + +} // namespace status_strings + +namespace misc_strings { + +const char name_value_separator[] = { ':', ' ' }; +const char crlf[] = { '\r', '\n' }; + +} // namespace misc_strings + +std::vector<asio::const_buffer> reply::to_buffers() +{ + std::vector<asio::const_buffer> buffers; + buffers.push_back(status_strings::to_buffer(status)); + for (std::size_t i = 0; i < headers.size(); ++i) + { + header& h = headers[i]; + buffers.push_back(asio::buffer(h.name)); + buffers.push_back(asio::buffer(misc_strings::name_value_separator)); + buffers.push_back(asio::buffer(h.value)); + buffers.push_back(asio::buffer(misc_strings::crlf)); + } + buffers.push_back(asio::buffer(misc_strings::crlf)); + buffers.push_back(asio::buffer(content)); + return buffers; +} + +namespace stock_replies { + +const char ok[] = ""; +const char created[] = + "<html>" + "<head><title>Created</title></head>" + "<body><h1>201 Created</h1></body>" + "</html>"; +const char accepted[] = + "<html>" + "<head><title>Accepted</title></head>" + "<body><h1>202 Accepted</h1></body>" + "</html>"; +const char no_content[] = + "<html>" + "<head><title>No Content</title></head>" + "<body><h1>204 Content</h1></body>" + "</html>"; +const char multiple_choices[] = + "<html>" + "<head><title>Multiple Choices</title></head>" + "<body><h1>300 Multiple Choices</h1></body>" + "</html>"; +const char moved_permanently[] = + "<html>" + "<head><title>Moved Permanently</title></head>" + "<body><h1>301 Moved Permanently</h1></body>" + "</html>"; +const char moved_temporarily[] = + "<html>" + "<head><title>Moved Temporarily</title></head>" + "<body><h1>302 Moved Temporarily</h1></body>" + "</html>"; +const char not_modified[] = + "<html>" + "<head><title>Not Modified</title></head>" + "<body><h1>304 Not Modified</h1></body>" + "</html>"; +const char bad_request[] = + "<html>" + "<head><title>Bad Request</title></head>" + "<body><h1>400 Bad Request</h1></body>" + "</html>"; +const char unauthorized[] = + "<html>" + "<head><title>Unauthorized</title></head>" + "<body><h1>401 Unauthorized</h1></body>" + "</html>"; +const char forbidden[] = + "<html>" + "<head><title>Forbidden</title></head>" + "<body><h1>403 Forbidden</h1></body>" + "</html>"; +const char not_found[] = + "<html>" + "<head><title>Not Found</title></head>" + "<body><h1>404 Not Found</h1></body>" + "</html>"; +const char internal_server_error[] = + "<html>" + "<head><title>Internal Server Error</title></head>" + "<body><h1>500 Internal Server Error</h1></body>" + "</html>"; +const char not_implemented[] = + "<html>" + "<head><title>Not Implemented</title></head>" + "<body><h1>501 Not Implemented</h1></body>" + "</html>"; +const char bad_gateway[] = + "<html>" + "<head><title>Bad Gateway</title></head>" + "<body><h1>502 Bad Gateway</h1></body>" + "</html>"; +const char service_unavailable[] = + "<html>" + "<head><title>Service Unavailable</title></head>" + "<body><h1>503 Service Unavailable</h1></body>" + "</html>"; + +std::string to_string(reply::status_type status) +{ + switch (status) + { + case reply::ok: + return ok; + case reply::created: + return created; + case reply::accepted: + return accepted; + case reply::no_content: + return no_content; + case reply::multiple_choices: + return multiple_choices; + case reply::moved_permanently: + return moved_permanently; + case reply::moved_temporarily: + return moved_temporarily; + case reply::not_modified: + return not_modified; + case reply::bad_request: + return bad_request; + case reply::unauthorized: + return unauthorized; + case reply::forbidden: + return forbidden; + case reply::not_found: + return not_found; + case reply::internal_server_error: + return internal_server_error; + case reply::not_implemented: + return not_implemented; + case reply::bad_gateway: + return bad_gateway; + case reply::service_unavailable: + return service_unavailable; + default: + return internal_server_error; + } +} + +} // namespace stock_replies + +reply reply::stock_reply(reply::status_type status) +{ + reply rep; + rep.status = status; + rep.content = stock_replies::to_string(status); + rep.headers.resize(2); + rep.headers[0].name = "Content-Length"; + rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size()); + rep.headers[1].name = "Content-Type"; + rep.headers[1].value = "text/html"; + return rep; +} + +} // namespace server3 +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server3/reply.hpp b/3rdparty/asio/src/examples/cpp03/http/server3/reply.hpp new file mode 100644 index 00000000000..9b057ca351d --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server3/reply.hpp @@ -0,0 +1,64 @@ +// +// reply.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER3_REPLY_HPP +#define HTTP_SERVER3_REPLY_HPP + +#include <string> +#include <vector> +#include <asio.hpp> +#include "header.hpp" + +namespace http { +namespace server3 { + +/// A reply to be sent to a client. +struct reply +{ + /// The status of the reply. + enum status_type + { + ok = 200, + created = 201, + accepted = 202, + no_content = 204, + multiple_choices = 300, + moved_permanently = 301, + moved_temporarily = 302, + not_modified = 304, + bad_request = 400, + unauthorized = 401, + forbidden = 403, + not_found = 404, + internal_server_error = 500, + not_implemented = 501, + bad_gateway = 502, + service_unavailable = 503 + } status; + + /// The headers to be included in the reply. + std::vector<header> headers; + + /// The content to be sent in the reply. + std::string content; + + /// Convert the reply into a vector of buffers. The buffers do not own the + /// underlying memory blocks, therefore the reply object must remain valid and + /// not be changed until the write operation has completed. + std::vector<asio::const_buffer> to_buffers(); + + /// Get a stock reply. + static reply stock_reply(status_type status); +}; + +} // namespace server3 +} // namespace http + +#endif // HTTP_SERVER3_REPLY_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server3/request.hpp b/3rdparty/asio/src/examples/cpp03/http/server3/request.hpp new file mode 100644 index 00000000000..a5b34e03f8a --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server3/request.hpp @@ -0,0 +1,34 @@ +// +// request.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER3_REQUEST_HPP +#define HTTP_SERVER3_REQUEST_HPP + +#include <string> +#include <vector> +#include "header.hpp" + +namespace http { +namespace server3 { + +/// A request received from a client. +struct request +{ + std::string method; + std::string uri; + int http_version_major; + int http_version_minor; + std::vector<header> headers; +}; + +} // namespace server3 +} // namespace http + +#endif // HTTP_SERVER3_REQUEST_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server3/request_handler.cpp b/3rdparty/asio/src/examples/cpp03/http/server3/request_handler.cpp new file mode 100644 index 00000000000..9feb1cf1e3c --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server3/request_handler.cpp @@ -0,0 +1,122 @@ +// +// request_handler.cpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "request_handler.hpp" +#include <fstream> +#include <sstream> +#include <string> +#include <boost/lexical_cast.hpp> +#include "mime_types.hpp" +#include "reply.hpp" +#include "request.hpp" + +namespace http { +namespace server3 { + +request_handler::request_handler(const std::string& doc_root) + : doc_root_(doc_root) +{ +} + +void request_handler::handle_request(const request& req, reply& rep) +{ + // Decode url to path. + std::string request_path; + if (!url_decode(req.uri, request_path)) + { + rep = reply::stock_reply(reply::bad_request); + return; + } + + // Request path must be absolute and not contain "..". + if (request_path.empty() || request_path[0] != '/' + || request_path.find("..") != std::string::npos) + { + rep = reply::stock_reply(reply::bad_request); + return; + } + + // If path ends in slash (i.e. is a directory) then add "index.html". + if (request_path[request_path.size() - 1] == '/') + { + request_path += "index.html"; + } + + // Determine the file extension. + std::size_t last_slash_pos = request_path.find_last_of("/"); + std::size_t last_dot_pos = request_path.find_last_of("."); + std::string extension; + if (last_dot_pos != std::string::npos && last_dot_pos > last_slash_pos) + { + extension = request_path.substr(last_dot_pos + 1); + } + + // Open the file to send back. + std::string full_path = doc_root_ + request_path; + std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary); + if (!is) + { + rep = reply::stock_reply(reply::not_found); + return; + } + + // Fill out the reply to be sent to the client. + rep.status = reply::ok; + char buf[512]; + while (is.read(buf, sizeof(buf)).gcount() > 0) + rep.content.append(buf, is.gcount()); + rep.headers.resize(2); + rep.headers[0].name = "Content-Length"; + rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size()); + rep.headers[1].name = "Content-Type"; + rep.headers[1].value = mime_types::extension_to_type(extension); +} + +bool request_handler::url_decode(const std::string& in, std::string& out) +{ + out.clear(); + out.reserve(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) + { + if (in[i] == '%') + { + if (i + 3 <= in.size()) + { + int value = 0; + std::istringstream is(in.substr(i + 1, 2)); + if (is >> std::hex >> value) + { + out += static_cast<char>(value); + i += 2; + } + else + { + return false; + } + } + else + { + return false; + } + } + else if (in[i] == '+') + { + out += ' '; + } + else + { + out += in[i]; + } + } + return true; +} + +} // namespace server3 +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server3/request_handler.hpp b/3rdparty/asio/src/examples/cpp03/http/server3/request_handler.hpp new file mode 100644 index 00000000000..bb94cad0d1d --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server3/request_handler.hpp @@ -0,0 +1,46 @@ +// +// request_handler.hpp +// ~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER3_REQUEST_HANDLER_HPP +#define HTTP_SERVER3_REQUEST_HANDLER_HPP + +#include <string> +#include <boost/noncopyable.hpp> + +namespace http { +namespace server3 { + +struct reply; +struct request; + +/// The common handler for all incoming requests. +class request_handler + : private boost::noncopyable +{ +public: + /// Construct with a directory containing files to be served. + explicit request_handler(const std::string& doc_root); + + /// Handle a request and produce a reply. + void handle_request(const request& req, reply& rep); + +private: + /// The directory containing the files to be served. + std::string doc_root_; + + /// Perform URL-decoding on a string. Returns false if the encoding was + /// invalid. + static bool url_decode(const std::string& in, std::string& out); +}; + +} // namespace server3 +} // namespace http + +#endif // HTTP_SERVER3_REQUEST_HANDLER_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server3/request_parser.cpp b/3rdparty/asio/src/examples/cpp03/http/server3/request_parser.cpp new file mode 100644 index 00000000000..98f91286fe8 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server3/request_parser.cpp @@ -0,0 +1,315 @@ +// +// request_parser.cpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "request_parser.hpp" +#include "request.hpp" + +namespace http { +namespace server3 { + +request_parser::request_parser() + : state_(method_start) +{ +} + +void request_parser::reset() +{ + state_ = method_start; +} + +boost::tribool request_parser::consume(request& req, char input) +{ + switch (state_) + { + case method_start: + if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + state_ = method; + req.method.push_back(input); + return boost::indeterminate; + } + case method: + if (input == ' ') + { + state_ = uri; + return boost::indeterminate; + } + else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + req.method.push_back(input); + return boost::indeterminate; + } + case uri: + if (input == ' ') + { + state_ = http_version_h; + return boost::indeterminate; + } + else if (is_ctl(input)) + { + return false; + } + else + { + req.uri.push_back(input); + return boost::indeterminate; + } + case http_version_h: + if (input == 'H') + { + state_ = http_version_t_1; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_t_1: + if (input == 'T') + { + state_ = http_version_t_2; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_t_2: + if (input == 'T') + { + state_ = http_version_p; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_p: + if (input == 'P') + { + state_ = http_version_slash; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_slash: + if (input == '/') + { + req.http_version_major = 0; + req.http_version_minor = 0; + state_ = http_version_major_start; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_major_start: + if (is_digit(input)) + { + req.http_version_major = req.http_version_major * 10 + input - '0'; + state_ = http_version_major; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_major: + if (input == '.') + { + state_ = http_version_minor_start; + return boost::indeterminate; + } + else if (is_digit(input)) + { + req.http_version_major = req.http_version_major * 10 + input - '0'; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_minor_start: + if (is_digit(input)) + { + req.http_version_minor = req.http_version_minor * 10 + input - '0'; + state_ = http_version_minor; + return boost::indeterminate; + } + else + { + return false; + } + case http_version_minor: + if (input == '\r') + { + state_ = expecting_newline_1; + return boost::indeterminate; + } + else if (is_digit(input)) + { + req.http_version_minor = req.http_version_minor * 10 + input - '0'; + return boost::indeterminate; + } + else + { + return false; + } + case expecting_newline_1: + if (input == '\n') + { + state_ = header_line_start; + return boost::indeterminate; + } + else + { + return false; + } + case header_line_start: + if (input == '\r') + { + state_ = expecting_newline_3; + return boost::indeterminate; + } + else if (!req.headers.empty() && (input == ' ' || input == '\t')) + { + state_ = header_lws; + return boost::indeterminate; + } + else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + req.headers.push_back(header()); + req.headers.back().name.push_back(input); + state_ = header_name; + return boost::indeterminate; + } + case header_lws: + if (input == '\r') + { + state_ = expecting_newline_2; + return boost::indeterminate; + } + else if (input == ' ' || input == '\t') + { + return boost::indeterminate; + } + else if (is_ctl(input)) + { + return false; + } + else + { + state_ = header_value; + req.headers.back().value.push_back(input); + return boost::indeterminate; + } + case header_name: + if (input == ':') + { + state_ = space_before_header_value; + return boost::indeterminate; + } + else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) + { + return false; + } + else + { + req.headers.back().name.push_back(input); + return boost::indeterminate; + } + case space_before_header_value: + if (input == ' ') + { + state_ = header_value; + return boost::indeterminate; + } + else + { + return false; + } + case header_value: + if (input == '\r') + { + state_ = expecting_newline_2; + return boost::indeterminate; + } + else if (is_ctl(input)) + { + return false; + } + else + { + req.headers.back().value.push_back(input); + return boost::indeterminate; + } + case expecting_newline_2: + if (input == '\n') + { + state_ = header_line_start; + return boost::indeterminate; + } + else + { + return false; + } + case expecting_newline_3: + return (input == '\n'); + default: + return false; + } +} + +bool request_parser::is_char(int c) +{ + return c >= 0 && c <= 127; +} + +bool request_parser::is_ctl(int c) +{ + return (c >= 0 && c <= 31) || (c == 127); +} + +bool request_parser::is_tspecial(int c) +{ + switch (c) + { + case '(': case ')': case '<': case '>': case '@': + case ',': case ';': case ':': case '\\': case '"': + case '/': case '[': case ']': case '?': case '=': + case '{': case '}': case ' ': case '\t': + return true; + default: + return false; + } +} + +bool request_parser::is_digit(int c) +{ + return c >= '0' && c <= '9'; +} + +} // namespace server3 +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server3/request_parser.hpp b/3rdparty/asio/src/examples/cpp03/http/server3/request_parser.hpp new file mode 100644 index 00000000000..b680eee037b --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server3/request_parser.hpp @@ -0,0 +1,95 @@ +// +// request_parser.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER3_REQUEST_PARSER_HPP +#define HTTP_SERVER3_REQUEST_PARSER_HPP + +#include <boost/logic/tribool.hpp> +#include <boost/tuple/tuple.hpp> + +namespace http { +namespace server3 { + +struct request; + +/// Parser for incoming requests. +class request_parser +{ +public: + /// Construct ready to parse the request method. + request_parser(); + + /// Reset to initial parser state. + void reset(); + + /// Parse some data. The tribool return value is true when a complete request + /// has been parsed, false if the data is invalid, indeterminate when more + /// data is required. The InputIterator return value indicates how much of the + /// input has been consumed. + template <typename InputIterator> + boost::tuple<boost::tribool, InputIterator> parse(request& req, + InputIterator begin, InputIterator end) + { + while (begin != end) + { + boost::tribool result = consume(req, *begin++); + if (result || !result) + return boost::make_tuple(result, begin); + } + boost::tribool result = boost::indeterminate; + return boost::make_tuple(result, begin); + } + +private: + /// Handle the next character of input. + boost::tribool consume(request& req, char input); + + /// Check if a byte is an HTTP character. + static bool is_char(int c); + + /// Check if a byte is an HTTP control character. + static bool is_ctl(int c); + + /// Check if a byte is defined as an HTTP tspecial character. + static bool is_tspecial(int c); + + /// Check if a byte is a digit. + static bool is_digit(int c); + + /// The current state of the parser. + enum state + { + method_start, + method, + uri, + http_version_h, + http_version_t_1, + http_version_t_2, + http_version_p, + http_version_slash, + http_version_major_start, + http_version_major, + http_version_minor_start, + http_version_minor, + expecting_newline_1, + header_line_start, + header_lws, + header_name, + space_before_header_value, + header_value, + expecting_newline_2, + expecting_newline_3 + } state_; +}; + +} // namespace server3 +} // namespace http + +#endif // HTTP_SERVER3_REQUEST_PARSER_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server3/server.cpp b/3rdparty/asio/src/examples/cpp03/http/server3/server.cpp new file mode 100644 index 00000000000..2a78bc9719e --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server3/server.cpp @@ -0,0 +1,89 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "server.hpp" +#include <boost/bind.hpp> +#include <boost/shared_ptr.hpp> +#include <vector> + +namespace http { +namespace server3 { + +server::server(const std::string& address, const std::string& port, + const std::string& doc_root, std::size_t thread_pool_size) + : thread_pool_size_(thread_pool_size), + signals_(io_context_), + acceptor_(io_context_), + new_connection_(), + request_handler_(doc_root) +{ + // Register to handle the signals that indicate when the server should exit. + // It is safe to register for the same signal multiple times in a program, + // provided all registration for the specified signal is made through Asio. + signals_.add(SIGINT); + signals_.add(SIGTERM); +#if defined(SIGQUIT) + signals_.add(SIGQUIT); +#endif // defined(SIGQUIT) + signals_.async_wait(boost::bind(&server::handle_stop, this)); + + // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR). + asio::ip::tcp::resolver resolver(io_context_); + asio::ip::tcp::endpoint endpoint = + *resolver.resolve(address, port).begin(); + acceptor_.open(endpoint.protocol()); + acceptor_.set_option(asio::ip::tcp::acceptor::reuse_address(true)); + acceptor_.bind(endpoint); + acceptor_.listen(); + + start_accept(); +} + +void server::run() +{ + // Create a pool of threads to run all of the io_contexts. + std::vector<boost::shared_ptr<asio::thread> > threads; + for (std::size_t i = 0; i < thread_pool_size_; ++i) + { + boost::shared_ptr<asio::thread> thread(new asio::thread( + boost::bind(&asio::io_context::run, &io_context_))); + threads.push_back(thread); + } + + // Wait for all threads in the pool to exit. + for (std::size_t i = 0; i < threads.size(); ++i) + threads[i]->join(); +} + +void server::start_accept() +{ + new_connection_.reset(new connection(io_context_, request_handler_)); + acceptor_.async_accept(new_connection_->socket(), + boost::bind(&server::handle_accept, this, + asio::placeholders::error)); +} + +void server::handle_accept(const asio::error_code& e) +{ + if (!e) + { + new_connection_->start(); + } + + start_accept(); +} + +void server::handle_stop() +{ + io_context_.stop(); +} + +} // namespace server3 +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server3/server.hpp b/3rdparty/asio/src/examples/cpp03/http/server3/server.hpp new file mode 100644 index 00000000000..c58c2433f65 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server3/server.hpp @@ -0,0 +1,70 @@ +// +// server.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER3_SERVER_HPP +#define HTTP_SERVER3_SERVER_HPP + +#include <asio.hpp> +#include <string> +#include <vector> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include "connection.hpp" +#include "request_handler.hpp" + +namespace http { +namespace server3 { + +/// The top-level class of the HTTP server. +class server + : private boost::noncopyable +{ +public: + /// Construct the server to listen on the specified TCP address and port, and + /// serve up files from the given directory. + explicit server(const std::string& address, const std::string& port, + const std::string& doc_root, std::size_t thread_pool_size); + + /// Run the server's io_context loop. + void run(); + +private: + /// Initiate an asynchronous accept operation. + void start_accept(); + + /// Handle completion of an asynchronous accept operation. + void handle_accept(const asio::error_code& e); + + /// Handle a request to stop the server. + void handle_stop(); + + /// The number of threads that will call io_context::run(). + std::size_t thread_pool_size_; + + /// The io_context used to perform asynchronous operations. + asio::io_context io_context_; + + /// The signal_set is used to register for process termination notifications. + asio::signal_set signals_; + + /// Acceptor used to listen for incoming connections. + asio::ip::tcp::acceptor acceptor_; + + /// The next connection to be accepted. + connection_ptr new_connection_; + + /// The handler for all incoming requests. + request_handler request_handler_; +}; + +} // namespace server3 +} // namespace http + +#endif // HTTP_SERVER3_SERVER_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server4/.gitignore b/3rdparty/asio/src/examples/cpp03/http/server4/.gitignore new file mode 100644 index 00000000000..0882fa6ced3 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server4/.gitignore @@ -0,0 +1,11 @@ +.deps +.dirstamp +*.o +*.obj +*.exe +*_server +*_client +*.ilk +*.manifest +*.pdb +*.tds diff --git a/3rdparty/asio/src/examples/cpp03/http/server4/file_handler.cpp b/3rdparty/asio/src/examples/cpp03/http/server4/file_handler.cpp new file mode 100644 index 00000000000..a4ac912b155 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server4/file_handler.cpp @@ -0,0 +1,122 @@ +// +// file_handler.cpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "file_handler.hpp" +#include <fstream> +#include <sstream> +#include <string> +#include <boost/lexical_cast.hpp> +#include "mime_types.hpp" +#include "reply.hpp" +#include "request.hpp" + +namespace http { +namespace server4 { + +file_handler::file_handler(const std::string& doc_root) + : doc_root_(doc_root) +{ +} + +void file_handler::operator()(const request& req, reply& rep) +{ + // Decode url to path. + std::string request_path; + if (!url_decode(req.uri, request_path)) + { + rep = reply::stock_reply(reply::bad_request); + return; + } + + // Request path must be absolute and not contain "..". + if (request_path.empty() || request_path[0] != '/' + || request_path.find("..") != std::string::npos) + { + rep = reply::stock_reply(reply::bad_request); + return; + } + + // If path ends in slash (i.e. is a directory) then add "index.html". + if (request_path[request_path.size() - 1] == '/') + { + request_path += "index.html"; + } + + // Determine the file extension. + std::size_t last_slash_pos = request_path.find_last_of("/"); + std::size_t last_dot_pos = request_path.find_last_of("."); + std::string extension; + if (last_dot_pos != std::string::npos && last_dot_pos > last_slash_pos) + { + extension = request_path.substr(last_dot_pos + 1); + } + + // Open the file to send back. + std::string full_path = doc_root_ + request_path; + std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary); + if (!is) + { + rep = reply::stock_reply(reply::not_found); + return; + } + + // Fill out the reply to be sent to the client. + rep.status = reply::ok; + char buf[512]; + while (is.read(buf, sizeof(buf)).gcount() > 0) + rep.content.append(buf, is.gcount()); + rep.headers.resize(2); + rep.headers[0].name = "Content-Length"; + rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size()); + rep.headers[1].name = "Content-Type"; + rep.headers[1].value = mime_types::extension_to_type(extension); +} + +bool file_handler::url_decode(const std::string& in, std::string& out) +{ + out.clear(); + out.reserve(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) + { + if (in[i] == '%') + { + if (i + 3 <= in.size()) + { + int value = 0; + std::istringstream is(in.substr(i + 1, 2)); + if (is >> std::hex >> value) + { + out += static_cast<char>(value); + i += 2; + } + else + { + return false; + } + } + else + { + return false; + } + } + else if (in[i] == '+') + { + out += ' '; + } + else + { + out += in[i]; + } + } + return true; +} + +} // namespace server4 +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server4/file_handler.hpp b/3rdparty/asio/src/examples/cpp03/http/server4/file_handler.hpp new file mode 100644 index 00000000000..22055dbcb1b --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server4/file_handler.hpp @@ -0,0 +1,44 @@ +// +// file_handler.hpp +// ~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER4_FILE_HANDLER_HPP +#define HTTP_SERVER4_FILE_HANDLER_HPP + +#include <string> + +namespace http { +namespace server4 { + +struct reply; +struct request; + +/// The common handler for all incoming requests. +class file_handler +{ +public: + /// Construct with a directory containing files to be served. + explicit file_handler(const std::string& doc_root); + + /// Handle a request and produce a reply. + void operator()(const request& req, reply& rep); + +private: + /// The directory containing the files to be served. + std::string doc_root_; + + /// Perform URL-decoding on a string. Returns false if the encoding was + /// invalid. + static bool url_decode(const std::string& in, std::string& out); +}; + +} // namespace server4 +} // namespace http + +#endif // HTTP_SERVER4_FILE_HANDLER_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server4/header.hpp b/3rdparty/asio/src/examples/cpp03/http/server4/header.hpp new file mode 100644 index 00000000000..9c9b89cca06 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server4/header.hpp @@ -0,0 +1,28 @@ +// +// header.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER4_HEADER_HPP +#define HTTP_SERVER4_HEADER_HPP + +#include <string> + +namespace http { +namespace server4 { + +struct header +{ + std::string name; + std::string value; +}; + +} // namespace server4 +} // namespace http + +#endif // HTTP_SERVER4_HEADER_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server4/main.cpp b/3rdparty/asio/src/examples/cpp03/http/server4/main.cpp new file mode 100644 index 00000000000..0ebe535ad17 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server4/main.cpp @@ -0,0 +1,58 @@ +// +// main.cpp +// ~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include <iostream> +#include <asio.hpp> +#include <boost/bind.hpp> +#include <signal.h> +#include "server.hpp" +#include "file_handler.hpp" + +int main(int argc, char* argv[]) +{ + try + { + // Check command line arguments. + if (argc != 4) + { + std::cerr << "Usage: http_server <address> <port> <doc_root>\n"; + std::cerr << " For IPv4, try:\n"; + std::cerr << " receiver 0.0.0.0 80 .\n"; + std::cerr << " For IPv6, try:\n"; + std::cerr << " receiver 0::0 80 .\n"; + return 1; + } + + asio::io_context io_context; + + // Launch the initial server coroutine. + http::server4::server(io_context, argv[1], argv[2], + http::server4::file_handler(argv[3]))(); + + // Wait for signals indicating time to shut down. + asio::signal_set signals(io_context); + signals.add(SIGINT); + signals.add(SIGTERM); +#if defined(SIGQUIT) + signals.add(SIGQUIT); +#endif // defined(SIGQUIT) + signals.async_wait(boost::bind( + &asio::io_context::stop, &io_context)); + + // Run the server. + io_context.run(); + } + catch (std::exception& e) + { + std::cerr << "exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/3rdparty/asio/src/examples/cpp03/http/server4/mime_types.cpp b/3rdparty/asio/src/examples/cpp03/http/server4/mime_types.cpp new file mode 100644 index 00000000000..903dffca59e --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server4/mime_types.cpp @@ -0,0 +1,46 @@ +// +// mime_types.cpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "mime_types.hpp" + +namespace http { +namespace server4 { +namespace mime_types { + +struct mapping +{ + const char* extension; + const char* mime_type; +} mappings[] = +{ + { "gif", "image/gif" }, + { "htm", "text/html" }, + { "html", "text/html" }, + { "jpg", "image/jpeg" }, + { "png", "image/png" }, + { 0, 0 } // Marks end of list. +}; + +std::string extension_to_type(const std::string& extension) +{ + for (mapping* m = mappings; m->extension; ++m) + { + if (m->extension == extension) + { + return m->mime_type; + } + } + + return "text/plain"; +} + +} // namespace mime_types +} // namespace server4 +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server4/mime_types.hpp b/3rdparty/asio/src/examples/cpp03/http/server4/mime_types.hpp new file mode 100644 index 00000000000..f702c6f859b --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server4/mime_types.hpp @@ -0,0 +1,27 @@ +// +// mime_types.hpp +// ~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER4_MIME_TYPES_HPP +#define HTTP_SERVER4_MIME_TYPES_HPP + +#include <string> + +namespace http { +namespace server4 { +namespace mime_types { + +/// Convert a file extension into a MIME type. +std::string extension_to_type(const std::string& extension); + +} // namespace mime_types +} // namespace server4 +} // namespace http + +#endif // HTTP_SERVER4_MIME_TYPES_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server4/reply.cpp b/3rdparty/asio/src/examples/cpp03/http/server4/reply.cpp new file mode 100644 index 00000000000..f508a46765f --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server4/reply.cpp @@ -0,0 +1,256 @@ +// +// reply.cpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "reply.hpp" +#include <string> +#include <boost/lexical_cast.hpp> + +namespace http { +namespace server4 { + +namespace status_strings { + +const std::string ok = + "HTTP/1.0 200 OK\r\n"; +const std::string created = + "HTTP/1.0 201 Created\r\n"; +const std::string accepted = + "HTTP/1.0 202 Accepted\r\n"; +const std::string no_content = + "HTTP/1.0 204 No Content\r\n"; +const std::string multiple_choices = + "HTTP/1.0 300 Multiple Choices\r\n"; +const std::string moved_permanently = + "HTTP/1.0 301 Moved Permanently\r\n"; +const std::string moved_temporarily = + "HTTP/1.0 302 Moved Temporarily\r\n"; +const std::string not_modified = + "HTTP/1.0 304 Not Modified\r\n"; +const std::string bad_request = + "HTTP/1.0 400 Bad Request\r\n"; +const std::string unauthorized = + "HTTP/1.0 401 Unauthorized\r\n"; +const std::string forbidden = + "HTTP/1.0 403 Forbidden\r\n"; +const std::string not_found = + "HTTP/1.0 404 Not Found\r\n"; +const std::string internal_server_error = + "HTTP/1.0 500 Internal Server Error\r\n"; +const std::string not_implemented = + "HTTP/1.0 501 Not Implemented\r\n"; +const std::string bad_gateway = + "HTTP/1.0 502 Bad Gateway\r\n"; +const std::string service_unavailable = + "HTTP/1.0 503 Service Unavailable\r\n"; + +asio::const_buffer to_buffer(reply::status_type status) +{ + switch (status) + { + case reply::ok: + return asio::buffer(ok); + case reply::created: + return asio::buffer(created); + case reply::accepted: + return asio::buffer(accepted); + case reply::no_content: + return asio::buffer(no_content); + case reply::multiple_choices: + return asio::buffer(multiple_choices); + case reply::moved_permanently: + return asio::buffer(moved_permanently); + case reply::moved_temporarily: + return asio::buffer(moved_temporarily); + case reply::not_modified: + return asio::buffer(not_modified); + case reply::bad_request: + return asio::buffer(bad_request); + case reply::unauthorized: + return asio::buffer(unauthorized); + case reply::forbidden: + return asio::buffer(forbidden); + case reply::not_found: + return asio::buffer(not_found); + case reply::internal_server_error: + return asio::buffer(internal_server_error); + case reply::not_implemented: + return asio::buffer(not_implemented); + case reply::bad_gateway: + return asio::buffer(bad_gateway); + case reply::service_unavailable: + return asio::buffer(service_unavailable); + default: + return asio::buffer(internal_server_error); + } +} + +} // namespace status_strings + +namespace misc_strings { + +const char name_value_separator[] = { ':', ' ' }; +const char crlf[] = { '\r', '\n' }; + +} // namespace misc_strings + +std::vector<asio::const_buffer> reply::to_buffers() +{ + std::vector<asio::const_buffer> buffers; + buffers.push_back(status_strings::to_buffer(status)); + for (std::size_t i = 0; i < headers.size(); ++i) + { + header& h = headers[i]; + buffers.push_back(asio::buffer(h.name)); + buffers.push_back(asio::buffer(misc_strings::name_value_separator)); + buffers.push_back(asio::buffer(h.value)); + buffers.push_back(asio::buffer(misc_strings::crlf)); + } + buffers.push_back(asio::buffer(misc_strings::crlf)); + buffers.push_back(asio::buffer(content)); + return buffers; +} + +namespace stock_replies { + +const char ok[] = ""; +const char created[] = + "<html>" + "<head><title>Created</title></head>" + "<body><h1>201 Created</h1></body>" + "</html>"; +const char accepted[] = + "<html>" + "<head><title>Accepted</title></head>" + "<body><h1>202 Accepted</h1></body>" + "</html>"; +const char no_content[] = + "<html>" + "<head><title>No Content</title></head>" + "<body><h1>204 Content</h1></body>" + "</html>"; +const char multiple_choices[] = + "<html>" + "<head><title>Multiple Choices</title></head>" + "<body><h1>300 Multiple Choices</h1></body>" + "</html>"; +const char moved_permanently[] = + "<html>" + "<head><title>Moved Permanently</title></head>" + "<body><h1>301 Moved Permanently</h1></body>" + "</html>"; +const char moved_temporarily[] = + "<html>" + "<head><title>Moved Temporarily</title></head>" + "<body><h1>302 Moved Temporarily</h1></body>" + "</html>"; +const char not_modified[] = + "<html>" + "<head><title>Not Modified</title></head>" + "<body><h1>304 Not Modified</h1></body>" + "</html>"; +const char bad_request[] = + "<html>" + "<head><title>Bad Request</title></head>" + "<body><h1>400 Bad Request</h1></body>" + "</html>"; +const char unauthorized[] = + "<html>" + "<head><title>Unauthorized</title></head>" + "<body><h1>401 Unauthorized</h1></body>" + "</html>"; +const char forbidden[] = + "<html>" + "<head><title>Forbidden</title></head>" + "<body><h1>403 Forbidden</h1></body>" + "</html>"; +const char not_found[] = + "<html>" + "<head><title>Not Found</title></head>" + "<body><h1>404 Not Found</h1></body>" + "</html>"; +const char internal_server_error[] = + "<html>" + "<head><title>Internal Server Error</title></head>" + "<body><h1>500 Internal Server Error</h1></body>" + "</html>"; +const char not_implemented[] = + "<html>" + "<head><title>Not Implemented</title></head>" + "<body><h1>501 Not Implemented</h1></body>" + "</html>"; +const char bad_gateway[] = + "<html>" + "<head><title>Bad Gateway</title></head>" + "<body><h1>502 Bad Gateway</h1></body>" + "</html>"; +const char service_unavailable[] = + "<html>" + "<head><title>Service Unavailable</title></head>" + "<body><h1>503 Service Unavailable</h1></body>" + "</html>"; + +std::string to_string(reply::status_type status) +{ + switch (status) + { + case reply::ok: + return ok; + case reply::created: + return created; + case reply::accepted: + return accepted; + case reply::no_content: + return no_content; + case reply::multiple_choices: + return multiple_choices; + case reply::moved_permanently: + return moved_permanently; + case reply::moved_temporarily: + return moved_temporarily; + case reply::not_modified: + return not_modified; + case reply::bad_request: + return bad_request; + case reply::unauthorized: + return unauthorized; + case reply::forbidden: + return forbidden; + case reply::not_found: + return not_found; + case reply::internal_server_error: + return internal_server_error; + case reply::not_implemented: + return not_implemented; + case reply::bad_gateway: + return bad_gateway; + case reply::service_unavailable: + return service_unavailable; + default: + return internal_server_error; + } +} + +} // namespace stock_replies + +reply reply::stock_reply(reply::status_type status) +{ + reply rep; + rep.status = status; + rep.content = stock_replies::to_string(status); + rep.headers.resize(2); + rep.headers[0].name = "Content-Length"; + rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size()); + rep.headers[1].name = "Content-Type"; + rep.headers[1].value = "text/html"; + return rep; +} + +} // namespace server4 +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server4/reply.hpp b/3rdparty/asio/src/examples/cpp03/http/server4/reply.hpp new file mode 100644 index 00000000000..02743a549be --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server4/reply.hpp @@ -0,0 +1,64 @@ +// +// reply.hpp +// ~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER4_REPLY_HPP +#define HTTP_SERVER4_REPLY_HPP + +#include <string> +#include <vector> +#include <asio.hpp> +#include "header.hpp" + +namespace http { +namespace server4 { + +/// A reply to be sent to a client. +struct reply +{ + /// The status of the reply. + enum status_type + { + ok = 200, + created = 201, + accepted = 202, + no_content = 204, + multiple_choices = 300, + moved_permanently = 301, + moved_temporarily = 302, + not_modified = 304, + bad_request = 400, + unauthorized = 401, + forbidden = 403, + not_found = 404, + internal_server_error = 500, + not_implemented = 501, + bad_gateway = 502, + service_unavailable = 503 + } status; + + /// The headers to be included in the reply. + std::vector<header> headers; + + /// The content to be sent in the reply. + std::string content; + + /// Convert the reply into a vector of buffers. The buffers do not own the + /// underlying memory blocks, therefore the reply object must remain valid and + /// not be changed until the write operation has completed. + std::vector<asio::const_buffer> to_buffers(); + + /// Get a stock reply. + static reply stock_reply(status_type status); +}; + +} // namespace server4 +} // namespace http + +#endif // HTTP_SERVER4_REPLY_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server4/request.hpp b/3rdparty/asio/src/examples/cpp03/http/server4/request.hpp new file mode 100644 index 00000000000..43947d7f09e --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server4/request.hpp @@ -0,0 +1,46 @@ +// +// request.hpp +// ~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER4_REQUEST_HPP +#define HTTP_SERVER4_REQUEST_HPP + +#include <string> +#include <vector> +#include "header.hpp" + +namespace http { +namespace server4 { + +/// A request received from a client. +struct request +{ + /// The request method, e.g. "GET", "POST". + std::string method; + + /// The requested URI, such as a path to a file. + std::string uri; + + /// Major version number, usually 1. + int http_version_major; + + /// Minor version number, usually 0 or 1. + int http_version_minor; + + /// The headers included with the request. + std::vector<header> headers; + + /// The optional content sent with the request. + std::string content; +}; + +} // namespace server4 +} // namespace http + +#endif // HTTP_SERVER4_REQUEST_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server4/request_parser.cpp b/3rdparty/asio/src/examples/cpp03/http/server4/request_parser.cpp new file mode 100644 index 00000000000..0ec31ad3939 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server4/request_parser.cpp @@ -0,0 +1,226 @@ +// +// request_parser.cpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "request_parser.hpp" +#include <algorithm> +#include <cctype> +#include <boost/lexical_cast.hpp> +#include "request.hpp" + +namespace http { +namespace server4 { + +// Enable the pseudo-keywords reenter, yield and fork. +#include <asio/yield.hpp> + +std::string request_parser::content_length_name_ = "Content-Length"; + +boost::tribool request_parser::consume(request& req, char c) +{ + reenter (this) + { + req.method.clear(); + req.uri.clear(); + req.http_version_major = 0; + req.http_version_minor = 0; + req.headers.clear(); + req.content.clear(); + content_length_ = 0; + + // Request method. + while (is_char(c) && !is_ctl(c) && !is_tspecial(c) && c != ' ') + { + req.method.push_back(c); + yield return boost::indeterminate; + } + if (req.method.empty()) + return false; + + // Space. + if (c != ' ') return false; + yield return boost::indeterminate; + + // URI. + while (!is_ctl(c) && c != ' ') + { + req.uri.push_back(c); + yield return boost::indeterminate; + } + if (req.uri.empty()) return false; + + // Space. + if (c != ' ') return false; + yield return boost::indeterminate; + + // HTTP protocol identifier. + if (c != 'H') return false; + yield return boost::indeterminate; + if (c != 'T') return false; + yield return boost::indeterminate; + if (c != 'T') return false; + yield return boost::indeterminate; + if (c != 'P') return false; + yield return boost::indeterminate; + + // Slash. + if (c != '/') return false; + yield return boost::indeterminate; + + // Major version number. + if (!is_digit(c)) return false; + while (is_digit(c)) + { + req.http_version_major = req.http_version_major * 10 + c - '0'; + yield return boost::indeterminate; + } + + // Dot. + if (c != '.') return false; + yield return boost::indeterminate; + + // Minor version number. + if (!is_digit(c)) return false; + while (is_digit(c)) + { + req.http_version_minor = req.http_version_minor * 10 + c - '0'; + yield return boost::indeterminate; + } + + // CRLF. + if (c != '\r') return false; + yield return boost::indeterminate; + if (c != '\n') return false; + yield return boost::indeterminate; + + // Headers. + while ((is_char(c) && !is_ctl(c) && !is_tspecial(c) && c != '\r') + || (c == ' ' || c == '\t')) + { + if (c == ' ' || c == '\t') + { + // Leading whitespace. Must be continuation of previous header's value. + if (req.headers.empty()) return false; + while (c == ' ' || c == '\t') + yield return boost::indeterminate; + } + else + { + // Start the next header. + req.headers.push_back(header()); + + // Header name. + while (is_char(c) && !is_ctl(c) && !is_tspecial(c) && c != ':') + { + req.headers.back().name.push_back(c); + yield return boost::indeterminate; + } + + // Colon and space separates the header name from the header value. + if (c != ':') return false; + yield return boost::indeterminate; + if (c != ' ') return false; + yield return boost::indeterminate; + } + + // Header value. + while (is_char(c) && !is_ctl(c) && c != '\r') + { + req.headers.back().value.push_back(c); + yield return boost::indeterminate; + } + + // CRLF. + if (c != '\r') return false; + yield return boost::indeterminate; + if (c != '\n') return false; + yield return boost::indeterminate; + } + + // CRLF. + if (c != '\r') return false; + yield return boost::indeterminate; + if (c != '\n') return false; + + // Check for optional Content-Length header. + for (std::size_t i = 0; i < req.headers.size(); ++i) + { + if (headers_equal(req.headers[i].name, content_length_name_)) + { + try + { + content_length_ = + boost::lexical_cast<std::size_t>(req.headers[i].value); + } + catch (boost::bad_lexical_cast&) + { + return false; + } + } + } + + // Content. + while (req.content.size() < content_length_) + { + yield return boost::indeterminate; + req.content.push_back(c); + } + } + + return true; +} + +// Disable the pseudo-keywords reenter, yield and fork. +#include <asio/unyield.hpp> + +bool request_parser::is_char(int c) +{ + return c >= 0 && c <= 127; +} + +bool request_parser::is_ctl(int c) +{ + return (c >= 0 && c <= 31) || (c == 127); +} + +bool request_parser::is_tspecial(int c) +{ + switch (c) + { + case '(': case ')': case '<': case '>': case '@': + case ',': case ';': case ':': case '\\': case '"': + case '/': case '[': case ']': case '?': case '=': + case '{': case '}': case ' ': case '\t': + return true; + default: + return false; + } +} + +bool request_parser::is_digit(int c) +{ + return c >= '0' && c <= '9'; +} + +bool request_parser::tolower_compare(char a, char b) +{ + return std::tolower(a) == std::tolower(b); +} + +bool request_parser::headers_equal(const std::string& a, const std::string& b) +{ + if (a.length() != b.length()) + return false; + + return std::equal(a.begin(), a.end(), b.begin(), + &request_parser::tolower_compare); +} + +} // namespace server4 +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server4/request_parser.hpp b/3rdparty/asio/src/examples/cpp03/http/server4/request_parser.hpp new file mode 100644 index 00000000000..a61356e36a6 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server4/request_parser.hpp @@ -0,0 +1,78 @@ +// +// request_parser.hpp +// ~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER4_REQUEST_PARSER_HPP +#define HTTP_SERVER4_REQUEST_PARSER_HPP + +#include <string> +#include <boost/logic/tribool.hpp> +#include <boost/tuple/tuple.hpp> +#include <asio/coroutine.hpp> + +namespace http { +namespace server4 { + +struct request; + +/// Parser for incoming requests. +class request_parser : asio::coroutine +{ +public: + /// Parse some data. The tribool return value is true when a complete request + /// has been parsed, false if the data is invalid, indeterminate when more + /// data is required. The InputIterator return value indicates how much of the + /// input has been consumed. + template <typename InputIterator> + boost::tuple<boost::tribool, InputIterator> parse(request& req, + InputIterator begin, InputIterator end) + { + while (begin != end) + { + boost::tribool result = consume(req, *begin++); + if (result || !result) + return boost::make_tuple(result, begin); + } + boost::tribool result = boost::indeterminate; + return boost::make_tuple(result, begin); + } + +private: + /// The name of the content length header. + static std::string content_length_name_; + + /// Content length as decoded from headers. Defaults to 0. + std::size_t content_length_; + + /// Handle the next character of input. + boost::tribool consume(request& req, char input); + + /// Check if a byte is an HTTP character. + static bool is_char(int c); + + /// Check if a byte is an HTTP control character. + static bool is_ctl(int c); + + /// Check if a byte is defined as an HTTP tspecial character. + static bool is_tspecial(int c); + + /// Check if a byte is a digit. + static bool is_digit(int c); + + /// Check if two characters are equal, without regard to case. + static bool tolower_compare(char a, char b); + + /// Check whether the two request header names match. + bool headers_equal(const std::string& a, const std::string& b); +}; + +} // namespace server4 +} // namespace http + +#endif // HTTP_SERVER4_REQUEST_PARSER_HPP diff --git a/3rdparty/asio/src/examples/cpp03/http/server4/server.cpp b/3rdparty/asio/src/examples/cpp03/http/server4/server.cpp new file mode 100644 index 00000000000..04d81a984b6 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server4/server.cpp @@ -0,0 +1,122 @@ +// +// server.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "server.hpp" +#include "request.hpp" +#include "reply.hpp" + +namespace http { +namespace server4 { + +server::server(asio::io_context& io_context, + const std::string& address, const std::string& port, + boost::function<void(const request&, reply&)> request_handler) + : request_handler_(request_handler) +{ + tcp::resolver resolver(io_context); + asio::ip::tcp::endpoint endpoint = + *resolver.resolve(address, port).begin(); + acceptor_.reset(new tcp::acceptor(io_context, endpoint)); +} + +// Enable the pseudo-keywords reenter, yield and fork. +#include <asio/yield.hpp> + +void server::operator()(asio::error_code ec, std::size_t length) +{ + // In this example we keep the error handling code in one place by + // hoisting it outside the coroutine. An alternative approach would be to + // check the value of ec after each yield for an asynchronous operation. + if (!ec) + { + // On reentering a coroutine, control jumps to the location of the last + // yield or fork. The argument to the "reenter" pseudo-keyword can be a + // pointer or reference to an object of type coroutine. + reenter (this) + { + // Loop to accept incoming connections. + do + { + // Create a new socket for the next incoming connection. + socket_.reset(new tcp::socket(acceptor_->get_executor().context())); + + // Accept a new connection. The "yield" pseudo-keyword saves the current + // line number and exits the coroutine's "reenter" block. We use the + // server coroutine as the completion handler for the async_accept + // operation. When the asynchronous operation completes, the io_context + // invokes the function call operator, we "reenter" the coroutine, and + // then control resumes at the following line. + yield acceptor_->async_accept(*socket_, *this); + + // We "fork" by cloning a new server coroutine to handle the connection. + // After forking we have a parent coroutine and a child coroutine. Both + // parent and child continue execution at the following line. They can + // be distinguished using the functions coroutine::is_parent() and + // coroutine::is_child(). + fork server(*this)(); + + // The parent continues looping to accept the next incoming connection. + // The child exits the loop and processes the connection. + } while (is_parent()); + + // Create the objects needed to receive a request on the connection. + buffer_.reset(new boost::array<char, 8192>); + request_.reset(new request); + + // Loop until a complete request (or an invalid one) has been received. + do + { + // Receive some more data. When control resumes at the following line, + // the ec and length parameters reflect the result of the asynchronous + // operation. + yield socket_->async_read_some(asio::buffer(*buffer_), *this); + + // Parse the data we just received. + boost::tie(valid_request_, boost::tuples::ignore) + = request_parser_.parse(*request_, + buffer_->data(), buffer_->data() + length); + + // An indeterminate result means we need more data, so keep looping. + } while (boost::indeterminate(valid_request_)); + + // Create the reply object that will be sent back to the client. + reply_.reset(new reply); + + if (valid_request_) + { + // A valid request was received. Call the user-supplied function object + // to process the request and compose a reply. + request_handler_(*request_, *reply_); + } + else + { + // The request was invalid. + *reply_ = reply::stock_reply(reply::bad_request); + } + + // Send the reply back to the client. + yield asio::async_write(*socket_, reply_->to_buffers(), *this); + + // Initiate graceful connection closure. + socket_->shutdown(tcp::socket::shutdown_both, ec); + } + } + + // If an error occurs then the coroutine is not reentered. Consequently, no + // new asynchronous operations are started. This means that all shared_ptr + // references will disappear and the resources associated with the coroutine + // will be destroyed automatically after this function call returns. +} + +// Disable the pseudo-keywords reenter, yield and fork. +#include <asio/unyield.hpp> + +} // namespace server4 +} // namespace http diff --git a/3rdparty/asio/src/examples/cpp03/http/server4/server.hpp b/3rdparty/asio/src/examples/cpp03/http/server4/server.hpp new file mode 100644 index 00000000000..580ece5e097 --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/http/server4/server.hpp @@ -0,0 +1,73 @@ +// +// server.hpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef HTTP_SERVER4_SERVER_HPP +#define HTTP_SERVER4_SERVER_HPP + +#include <asio.hpp> +#include <string> +#include <boost/array.hpp> +#include <boost/function.hpp> +#include <boost/shared_ptr.hpp> +#include "request_parser.hpp" + +namespace http { +namespace server4 { + +struct request; +struct reply; + +/// The top-level coroutine of the HTTP server. +class server : asio::coroutine +{ +public: + /// Construct the server to listen on the specified TCP address and port, and + /// serve up files from the given directory. + explicit server(asio::io_context& io_context, + const std::string& address, const std::string& port, + boost::function<void(const request&, reply&)> request_handler); + + /// Perform work associated with the server. + void operator()( + asio::error_code ec = asio::error_code(), + std::size_t length = 0); + +private: + typedef asio::ip::tcp tcp; + + /// The user-supplied handler for all incoming requests. + boost::function<void(const request&, reply&)> request_handler_; + + /// Acceptor used to listen for incoming connections. + boost::shared_ptr<tcp::acceptor> acceptor_; + + /// The current connection from a client. + boost::shared_ptr<tcp::socket> socket_; + + /// Buffer for incoming data. + boost::shared_ptr<boost::array<char, 8192> > buffer_; + + /// The incoming request. + boost::shared_ptr<request> request_; + + /// Whether the request is valid or not. + boost::tribool valid_request_; + + /// The parser for the incoming request. + request_parser request_parser_; + + /// The reply to be sent back to the client. + boost::shared_ptr<reply> reply_; +}; + +} // namespace server4 +} // namespace http + +#endif // HTTP_SERVER4_SERVER_HPP |