diff options
-rw-r--r-- | scripts/src/lib.lua | 32 | ||||
-rw-r--r-- | scripts/src/main.lua | 1 | ||||
-rw-r--r-- | src/lib/http/connection.cpp | 87 | ||||
-rw-r--r-- | src/lib/http/connection.hpp | 72 | ||||
-rw-r--r-- | src/lib/http/connection_manager.cpp | 33 | ||||
-rw-r--r-- | src/lib/http/connection_manager.hpp | 41 | ||||
-rw-r--r-- | src/lib/http/header.hpp | 21 | ||||
-rw-r--r-- | src/lib/http/mime_types.cpp | 106 | ||||
-rw-r--r-- | src/lib/http/mime_types.hpp | 20 | ||||
-rw-r--r-- | src/lib/http/reply.cpp | 248 | ||||
-rw-r--r-- | src/lib/http/reply.hpp | 57 | ||||
-rw-r--r-- | src/lib/http/request.hpp | 27 | ||||
-rw-r--r-- | src/lib/http/request_handler.cpp | 125 | ||||
-rw-r--r-- | src/lib/http/request_handler.hpp | 42 | ||||
-rw-r--r-- | src/lib/http/request_parser.cpp | 308 | ||||
-rw-r--r-- | src/lib/http/request_parser.hpp | 89 | ||||
-rw-r--r-- | src/lib/http/server.cpp | 86 | ||||
-rw-r--r-- | src/lib/http/server.hpp | 57 |
18 files changed, 0 insertions, 1452 deletions
diff --git a/scripts/src/lib.lua b/scripts/src/lib.lua index b89b58655d7..51b92d098d2 100644 --- a/scripts/src/lib.lua +++ b/scripts/src/lib.lua @@ -100,35 +100,3 @@ project "utils" MAME_DIR .. "src/lib/util/zippath.cpp", MAME_DIR .. "src/lib/util/zippath.h", } - - -project "http" - uuid "d7930d44-21d1-4c5d-b6af-582c141cd23a" - kind (LIBTYPE) - - addprojectflags() - - includedirs { - MAME_DIR .. "3rdparty", - MAME_DIR .. "3rdparty/asio/include", - MAME_DIR .. "src/osd", - } - - files { - MAME_DIR .. "src/lib/http/connection.cpp", - MAME_DIR .. "src/lib/http/connection.hpp", - MAME_DIR .. "src/lib/http/connection_manager.cpp", - MAME_DIR .. "src/lib/http/connection_manager.hpp", - MAME_DIR .. "src/lib/http/header.hpp", - MAME_DIR .. "src/lib/http/mime_types.cpp", - MAME_DIR .. "src/lib/http/mime_types.hpp", - MAME_DIR .. "src/lib/http/reply.cpp", - MAME_DIR .. "src/lib/http/reply.hpp", - MAME_DIR .. "src/lib/http/request.hpp", - MAME_DIR .. "src/lib/http/request_handler.cpp", - MAME_DIR .. "src/lib/http/request_handler.hpp", - MAME_DIR .. "src/lib/http/request_parser.cpp", - MAME_DIR .. "src/lib/http/request_parser.hpp", - MAME_DIR .. "src/lib/http/server.cpp", - MAME_DIR .. "src/lib/http/server.hpp", - } diff --git a/scripts/src/main.lua b/scripts/src/main.lua index c3c4ac235be..a5559ef151b 100644 --- a/scripts/src/main.lua +++ b/scripts/src/main.lua @@ -234,7 +234,6 @@ end if (STANDALONE~=true) then links { "frontend", - "http", } end if (MACHINES["NETLIST"]~=null) then diff --git a/src/lib/http/connection.cpp b/src/lib/http/connection.cpp deleted file mode 100644 index bb1c07cfa06..00000000000 --- a/src/lib/http/connection.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// license:Boost -// copyright-holders:Christopher M. Kohlhoff - -#include "connection.hpp" -#include <utility> -#include <vector> -#include "connection_manager.hpp" -#include "request_handler.hpp" - -namespace http { -namespace server { - -connection::connection(asio::ip::tcp::socket socket, - connection_manager& manager, request_handler& handler) - : m_socket(std::move(socket)), - m_connection_manager(manager), - m_request_handler(handler) -{ -} - -void connection::start() -{ - do_read(); -} - -void connection::stop() -{ - m_socket.close(); -} - -void connection::do_read() -{ - auto self(shared_from_this()); - m_socket.async_read_some(asio::buffer(m_buffer), - [this, self](std::error_code ec, std::size_t bytes_transferred) - { - if (!ec) - { - request_parser::result_type result; - std::tie(result, std::ignore) = m_request_parser.parse( - m_request, m_buffer.data(), m_buffer.data() + bytes_transferred); - - if (result == request_parser::good) - { - m_request_handler.handle_request(m_request, m_reply); - do_write(); - } - else if (result == request_parser::bad) - { - m_reply = reply::stock_reply(reply::bad_request); - do_write(); - } - else - { - do_read(); - } - } - else if (ec != asio::error::operation_aborted) - { - m_connection_manager.stop(shared_from_this()); - } - }); -} - -void connection::do_write() -{ - auto self(shared_from_this()); - asio::async_write(m_socket, m_reply.to_buffers(), - [this, self](std::error_code ec, std::size_t) - { - if (!ec) - { - // Initiate graceful connection closure. - asio::error_code ignored_ec; - m_socket.shutdown(asio::ip::tcp::socket::shutdown_both, - ignored_ec); - } - - if (ec != asio::error::operation_aborted) - { - m_connection_manager.stop(shared_from_this()); - } - }); -} - -} // namespace server -} // namespace http diff --git a/src/lib/http/connection.hpp b/src/lib/http/connection.hpp deleted file mode 100644 index 6ad1768242e..00000000000 --- a/src/lib/http/connection.hpp +++ /dev/null @@ -1,72 +0,0 @@ -// license:Boost -// copyright-holders:Christopher M. Kohlhoff - -#ifndef HTTP_CONNECTION_HPP -#define HTTP_CONNECTION_HPP - -#include <array> -#include <memory> -#include "asio.h" -#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 std::enable_shared_from_this<connection> -{ -public: - connection(const connection&) = delete; - connection& operator=(const connection&) = delete; - - /// Construct a connection with the given socket. - explicit connection(asio::ip::tcp::socket socket, - connection_manager& manager, request_handler& handler); - - /// Start the first asynchronous operation for the connection. - void start(); - - /// Stop all asynchronous operations associated with the connection. - void stop(); - -private: - /// Perform an asynchronous read operation. - void do_read(); - - /// Perform an asynchronous write operation. - void do_write(); - - /// Socket for the connection. - asio::ip::tcp::socket m_socket; - - /// The manager for this connection. - connection_manager& m_connection_manager; - - /// The handler used to process the incoming request. - request_handler& m_request_handler; - - /// Buffer for incoming data. - std::array<char, 8192> m_buffer; - - /// The incoming request. - request m_request; - - /// The parser for the incoming request. - request_parser m_request_parser; - - /// The reply to be sent back to the client. - reply m_reply; -}; - -typedef std::shared_ptr<connection> connection_ptr; - -} // namespace server -} // namespace http - -#endif // HTTP_CONNECTION_HPP diff --git a/src/lib/http/connection_manager.cpp b/src/lib/http/connection_manager.cpp deleted file mode 100644 index 97464bf3639..00000000000 --- a/src/lib/http/connection_manager.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// license:Boost -// copyright-holders:Christopher M. Kohlhoff - -#include "connection_manager.hpp" - -namespace http { -namespace server { - -connection_manager::connection_manager() -{ -} - -void connection_manager::start(connection_ptr c) -{ - m_connections.insert(c); - c->start(); -} - -void connection_manager::stop(connection_ptr c) -{ - m_connections.erase(c); - c->stop(); -} - -void connection_manager::stop_all() -{ - for (auto c: m_connections) - c->stop(); - m_connections.clear(); -} - -} // namespace server -} // namespace http diff --git a/src/lib/http/connection_manager.hpp b/src/lib/http/connection_manager.hpp deleted file mode 100644 index ad215d05c69..00000000000 --- a/src/lib/http/connection_manager.hpp +++ /dev/null @@ -1,41 +0,0 @@ -// license:Boost -// copyright-holders:Christopher M. Kohlhoff - -#ifndef HTTP_CONNECTION_MANAGER_HPP -#define HTTP_CONNECTION_MANAGER_HPP - -#include <set> -#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 -{ -public: - connection_manager(const connection_manager&) = delete; - connection_manager& operator=(const connection_manager&) = delete; - - /// Construct a connection manager. - connection_manager(); - - /// 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> m_connections; -}; - -} // namespace server -} // namespace http - -#endif // HTTP_CONNECTION_MANAGER_HPP diff --git a/src/lib/http/header.hpp b/src/lib/http/header.hpp deleted file mode 100644 index 7cad96f2439..00000000000 --- a/src/lib/http/header.hpp +++ /dev/null @@ -1,21 +0,0 @@ -// license:Boost -// copyright-holders:Christopher M. Kohlhoff - -#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/src/lib/http/mime_types.cpp b/src/lib/http/mime_types.cpp deleted file mode 100644 index 3ca649dfb80..00000000000 --- a/src/lib/http/mime_types.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// license:Boost -// copyright-holders:Christopher M. Kohlhoff - -#include "mime_types.hpp" - -namespace http { -namespace server { -namespace mime_types { - -struct mapping -{ - const char* extension; - const char* mime_type; -} mappings[] = -{ - { "aac", "audio/aac" }, - { "aat", "application/font-sfnt" }, - { "aif", "audio/x-aif" }, - { "arj", "application/x-arj-compressed" }, - { "asf", "video/x-ms-asf" }, - { "avi", "video/x-msvideo" }, - { "bmp", "image/bmp" }, - { "cff", "application/font-sfnt" }, - { "css", "text/css" }, - { "csv", "text/csv" }, - { "doc", "application/msword" }, - { "eps", "application/postscript" }, - { "exe", "application/octet-stream" }, - { "gif", "image/gif" }, - { "gz", "application/x-gunzip" }, - { "htm", "text/html" }, - { "html", "text/html" }, - { "ico", "image/x-icon" }, - { "ief", "image/ief" }, - { "jpeg", "image/jpeg" }, - { "jpg", "image/jpeg" }, - { "jpm", "image/jpm" }, - { "jpx", "image/jpx" }, - { "js", "application/javascript" }, - { "json", "application/json" }, - { "m3u", "audio/x-mpegurl" }, - { "m4v", "video/x-m4v" }, - { "mid", "audio/x-midi" }, - { "mov", "video/quicktime" }, - { "mp3", "audio/mpeg" }, - { "mp4", "video/mp4" }, - { "mpeg", "video/mpeg" }, - { "mpg", "video/mpeg" }, - { "oga", "audio/ogg" }, - { "ogg", "audio/ogg" }, - { "ogv", "video/ogg" }, - { "otf", "application/font-sfnt" }, - { "pct", "image/x-pct" }, - { "pdf", "application/pdf" }, - { "pfr", "application/font-tdpfr" }, - { "pict", "image/pict" }, - { "png", "image/png" }, - { "ppt", "application/x-mspowerpoint" }, - { "ps", "application/postscript" }, - { "qt", "video/quicktime" }, - { "ra", "audio/x-pn-realaudio" }, - { "ram", "audio/x-pn-realaudio" }, - { "rar", "application/x-arj-compressed" }, - { "rgb", "image/x-rgb" }, - { "rtf", "application/rtf" }, - { "sgm", "text/sgml" }, - { "shtm", "text/html" }, - { "shtml", "text/html" }, - { "sil", "application/font-sfnt" }, - { "svg", "image/svg+xml" }, - { "swf", "application/x-shockwave-flash" }, - { "tar", "application/x-tar" }, - { "tgz", "application/x-tar-gz" }, - { "tif", "image/tiff" }, - { "tiff", "image/tiff" }, - { "torrent", "application/x-bittorrent" }, - { "ttf", "application/font-sfnt" }, - { "txt", "text/plain" }, - { "wav", "audio/x-wav" }, - { "webm", "video/webm" }, - { "woff", "application/font-woff" }, - { "wrl", "model/vrml" }, - { "xhtml", "application/xhtml+xml" }, - { "xls", "application/x-msexcel" }, - { "xml", "text/xml" }, - { "xsl", "application/xml" }, - { "xslt", "application/xml" }, - { "zip", "application/x-zip-compressed" } -}; - -std::string extension_to_type(const std::string& extension) -{ - for (mapping m: mappings) - { - if (m.extension == extension) - { - return m.mime_type; - } - } - - return "text/plain"; -} - -} // namespace mime_types -} // namespace server -} // namespace http diff --git a/src/lib/http/mime_types.hpp b/src/lib/http/mime_types.hpp deleted file mode 100644 index c0314f22dd2..00000000000 --- a/src/lib/http/mime_types.hpp +++ /dev/null @@ -1,20 +0,0 @@ -// license:Boost -// copyright-holders:Christopher M. Kohlhoff - -#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/src/lib/http/reply.cpp b/src/lib/http/reply.cpp deleted file mode 100644 index 0997865f336..00000000000 --- a/src/lib/http/reply.cpp +++ /dev/null @@ -1,248 +0,0 @@ -// license:Boost -// copyright-holders:Christopher M. Kohlhoff - -#include "reply.hpp" -#include <string> - -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 = std::to_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/src/lib/http/reply.hpp b/src/lib/http/reply.hpp deleted file mode 100644 index 7e05752f3e0..00000000000 --- a/src/lib/http/reply.hpp +++ /dev/null @@ -1,57 +0,0 @@ -// license:Boost -// copyright-holders:Christopher M. Kohlhoff - -#ifndef HTTP_REPLY_HPP -#define HTTP_REPLY_HPP - -#include <string> -#include <vector> -#include "asio.h" -#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/src/lib/http/request.hpp b/src/lib/http/request.hpp deleted file mode 100644 index fc824435cb3..00000000000 --- a/src/lib/http/request.hpp +++ /dev/null @@ -1,27 +0,0 @@ -// license:Boost -// copyright-holders:Christopher M. Kohlhoff - -#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/src/lib/http/request_handler.cpp b/src/lib/http/request_handler.cpp deleted file mode 100644 index 3bc4f86de29..00000000000 --- a/src/lib/http/request_handler.cpp +++ /dev/null @@ -1,125 +0,0 @@ -// license:Boost -// copyright-holders:Christopher M. Kohlhoff - -#include "request_handler.hpp" -#include <fstream> -#include <sstream> -#include <string> -#include "mime_types.hpp" -#include "reply.hpp" -#include "request.hpp" - -namespace http { -namespace server { - -request_handler::request_handler(const std::string& doc_root) - : m_doc_root(doc_root) -{ -} - -bool request_handler::serve_static_content(std::string& request_path,reply& rep) const -{ - // 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"; - } - - std::size_t last_qmark_pos = request_path.find_last_of("?"); - if (last_qmark_pos!=std::string::npos) - request_path = request_path.substr(0, last_qmark_pos - 1); - - // 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 = m_doc_root + request_path; - std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary); - if (!is) - { - return false; - } - - // 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 = std::to_string(rep.content.size()); - rep.headers[1].name = "Content-Type"; - rep.headers[1].value = mime_types::extension_to_type(extension); - return true; -} - -void request_handler::handle_request(const request& req, reply& rep) const -{ - // 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 (!serve_static_content(request_path, rep)) - { - rep = reply::stock_reply(reply::not_found); - } -} - -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/src/lib/http/request_handler.hpp b/src/lib/http/request_handler.hpp deleted file mode 100644 index 0554aa114c5..00000000000 --- a/src/lib/http/request_handler.hpp +++ /dev/null @@ -1,42 +0,0 @@ -// license:Boost -// copyright-holders:Christopher M. Kohlhoff - -#ifndef HTTP_REQUEST_HANDLER_HPP -#define HTTP_REQUEST_HANDLER_HPP - -#include <string> - -namespace http { -namespace server { - -struct reply; -struct request; - -/// The common handler for all incoming requests. -class request_handler -{ -public: - request_handler(const request_handler&) = delete; - request_handler& operator=(const request_handler&) = delete; - - /// 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) const; - - bool serve_static_content(std::string& request_path, reply& rep) const; - -private: - /// The directory containing the files to be served. - std::string m_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/src/lib/http/request_parser.cpp b/src/lib/http/request_parser.cpp deleted file mode 100644 index f1e69ed677d..00000000000 --- a/src/lib/http/request_parser.cpp +++ /dev/null @@ -1,308 +0,0 @@ -// license:Boost -// copyright-holders:Christopher M. Kohlhoff - -#include "request_parser.hpp" -#include "request.hpp" - -namespace http { -namespace server { - -request_parser::request_parser() - : m_state(method_start) -{ -} - -void request_parser::reset() -{ - m_state = method_start; -} - -request_parser::result_type request_parser::consume(request& req, char input) -{ - switch (m_state) - { - case method_start: - if (!is_char(input) || is_ctl(input) || is_tspecial(input)) - { - return bad; - } - else - { - m_state = method; - req.method.push_back(input); - return indeterminate; - } - case method: - if (input == ' ') - { - m_state = uri; - return indeterminate; - } - else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) - { - return bad; - } - else - { - req.method.push_back(input); - return indeterminate; - } - case uri: - if (input == ' ') - { - m_state = http_version_h; - return indeterminate; - } - else if (is_ctl(input)) - { - return bad; - } - else - { - req.uri.push_back(input); - return indeterminate; - } - case http_version_h: - if (input == 'H') - { - m_state = http_version_t_1; - return indeterminate; - } - else - { - return bad; - } - case http_version_t_1: - if (input == 'T') - { - m_state = http_version_t_2; - return indeterminate; - } - else - { - return bad; - } - case http_version_t_2: - if (input == 'T') - { - m_state = http_version_p; - return indeterminate; - } - else - { - return bad; - } - case http_version_p: - if (input == 'P') - { - m_state = http_version_slash; - return indeterminate; - } - else - { - return bad; - } - case http_version_slash: - if (input == '/') - { - req.http_version_major = 0; - req.http_version_minor = 0; - m_state = http_version_major_start; - return indeterminate; - } - else - { - return bad; - } - case http_version_major_start: - if (is_digit(input)) - { - req.http_version_major = req.http_version_major * 10 + input - '0'; - m_state = http_version_major; - return indeterminate; - } - else - { - return bad; - } - case http_version_major: - if (input == '.') - { - m_state = http_version_minor_start; - return indeterminate; - } - else if (is_digit(input)) - { - req.http_version_major = req.http_version_major * 10 + input - '0'; - return indeterminate; - } - else - { - return bad; - } - case http_version_minor_start: - if (is_digit(input)) - { - req.http_version_minor = req.http_version_minor * 10 + input - '0'; - m_state = http_version_minor; - return indeterminate; - } - else - { - return bad; - } - case http_version_minor: - if (input == '\r') - { - m_state = expecting_newline_1; - return indeterminate; - } - else if (is_digit(input)) - { - req.http_version_minor = req.http_version_minor * 10 + input - '0'; - return indeterminate; - } - else - { - return bad; - } - case expecting_newline_1: - if (input == '\n') - { - m_state = header_line_start; - return indeterminate; - } - else - { - return bad; - } - case header_line_start: - if (input == '\r') - { - m_state = expecting_newline_3; - return indeterminate; - } - else if (!req.headers.empty() && (input == ' ' || input == '\t')) - { - m_state = header_lws; - return indeterminate; - } - else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) - { - return bad; - } - else - { - req.headers.push_back(header()); - req.headers.back().name.push_back(input); - m_state = header_name; - return indeterminate; - } - case header_lws: - if (input == '\r') - { - m_state = expecting_newline_2; - return indeterminate; - } - else if (input == ' ' || input == '\t') - { - return indeterminate; - } - else if (is_ctl(input)) - { - return bad; - } - else - { - m_state = header_value; - req.headers.back().value.push_back(input); - return indeterminate; - } - case header_name: - if (input == ':') - { - m_state = space_before_header_value; - return indeterminate; - } - else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) - { - return bad; - } - else - { - req.headers.back().name.push_back(input); - return indeterminate; - } - case space_before_header_value: - if (input == ' ') - { - m_state = header_value; - return indeterminate; - } - else - { - return bad; - } - case header_value: - if (input == '\r') - { - m_state = expecting_newline_2; - return indeterminate; - } - else if (is_ctl(input)) - { - return bad; - } - else - { - req.headers.back().value.push_back(input); - return indeterminate; - } - case expecting_newline_2: - if (input == '\n') - { - m_state = header_line_start; - return indeterminate; - } - else - { - return bad; - } - case expecting_newline_3: - return (input == '\n') ? good : bad; - default: - return bad; - } -} - -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/src/lib/http/request_parser.hpp b/src/lib/http/request_parser.hpp deleted file mode 100644 index 828091c6bd3..00000000000 --- a/src/lib/http/request_parser.hpp +++ /dev/null @@ -1,89 +0,0 @@ -// license:Boost -// copyright-holders:Christopher M. Kohlhoff - -#ifndef HTTP_REQUEST_PARSER_HPP -#define HTTP_REQUEST_PARSER_HPP - -#include <tuple> - -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(); - - /// Result of parse. - enum result_type { good, bad, indeterminate }; - - /// Parse some data. The enum return value is good when a complete request has - /// been parsed, bad 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> - std::tuple<result_type, InputIterator> parse(request& req, - InputIterator begin, InputIterator end) - { - while (begin != end) - { - result_type result = consume(req, *begin++); - if (result == good || result == bad) - return std::make_tuple(result, begin); - } - return std::make_tuple(indeterminate, begin); - } - -private: - /// Handle the next character of input. - result_type 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 - } m_state; -}; - -} // namespace server -} // namespace http - -#endif // HTTP_REQUEST_PARSER_HPP diff --git a/src/lib/http/server.cpp b/src/lib/http/server.cpp deleted file mode 100644 index a096aa58517..00000000000 --- a/src/lib/http/server.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// license:Boost -// copyright-holders:Christopher M. Kohlhoff - -#include "server.hpp" -#include <signal.h> -#include <utility> - -namespace http { -namespace server { - -server::server(const std::string& address, const std::string& port, const std::string& doc_root) - : m_io_context(1), - m_signals(m_io_context), - m_acceptor(m_io_context), - m_connection_manager(), - m_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. - m_signals.add(SIGINT); - m_signals.add(SIGTERM); -#if defined(SIGQUIT) - m_signals.add(SIGQUIT); -#endif // defined(SIGQUIT) - - do_await_stop(); - - // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR). - asio::ip::tcp::resolver resolver(m_io_context); - asio::ip::tcp::endpoint endpoint = - *resolver.resolve(address, port).begin(); - m_acceptor.open(endpoint.protocol()); - m_acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true)); - m_acceptor.bind(endpoint); - m_acceptor.listen(); - - do_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. - m_io_context.run(); -} - -void server::do_accept() -{ - m_acceptor.async_accept( - [this](std::error_code ec, asio::ip::tcp::socket socket) - { - // Check whether the server was stopped by a signal before this - // completion handler had a chance to run. - if (!m_acceptor.is_open()) - { - return; - } - - if (!ec) - { - m_connection_manager.start(std::make_shared<connection>( - std::move(socket), m_connection_manager, m_request_handler)); - } - - do_accept(); - }); -} - -void server::do_await_stop() -{ - m_signals.async_wait( - [this](std::error_code /*ec*/, int /*signo*/) - { - // The server is stopped by cancelling all outstanding asynchronous - // operations. Once all operations have finished the io_context::run() - // call will exit. - m_acceptor.close(); - m_connection_manager.stop_all(); - }); -} - -} // namespace server -} // namespace http diff --git a/src/lib/http/server.hpp b/src/lib/http/server.hpp deleted file mode 100644 index 7c6483acfe3..00000000000 --- a/src/lib/http/server.hpp +++ /dev/null @@ -1,57 +0,0 @@ -// license:Boost -// copyright-holders:Christopher M. Kohlhoff - -#ifndef HTTP_SERVER_HPP -#define HTTP_SERVER_HPP - -#include "asio.h" -#include <string> -#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 -{ -public: - server(const server&) = delete; - server& operator=(const server&) = delete; - - /// 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: - /// Perform an asynchronous accept operation. - void do_accept(); - - /// Wait for a request to stop the server. - void do_await_stop(); - - /// The io_context used to perform asynchronous operations. - asio::io_context m_io_context; - - /// The signal_set is used to register for process termination notifications. - asio::signal_set m_signals; - - /// Acceptor used to listen for incoming connections. - asio::ip::tcp::acceptor m_acceptor; - - /// The connection manager which owns all live connections. - connection_manager m_connection_manager; - - /// The handler for all incoming requests. - request_handler m_request_handler; -}; - -} // namespace server -} // namespace http - -#endif // HTTP_SERVER_HPP |