forked from boostorg/beast
New server-framework, full featured server example:
A new server framework is introduced, allowing users to quickly get off the ground. Example servers are refactored to use the common framework.
This commit is contained in:
248
example/server-framework/file_service.hpp
Normal file
248
example/server-framework/file_service.hpp
Normal file
@@ -0,0 +1,248 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail 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 BEAST_EXAMPLE_SERVER_FILE_SERVICE_HPP
|
||||
#define BEAST_EXAMPLE_SERVER_FILE_SERVICE_HPP
|
||||
|
||||
#include "file_body.hpp"
|
||||
#include "framework.hpp"
|
||||
|
||||
#include <beast/core/string.hpp>
|
||||
#include <beast/http/empty_body.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace framework {
|
||||
|
||||
/** An HTTP service which delivers files from a root directory.
|
||||
|
||||
This service will accept GET and HEAD requests for files,
|
||||
and deliver them as responses. The service constructs with
|
||||
the location on the file system to act as the root for the
|
||||
tree of files to serve.
|
||||
|
||||
Meets the requirements of @b Service
|
||||
*/
|
||||
class file_service
|
||||
{
|
||||
// The path to serve files from
|
||||
boost::filesystem::path root_;
|
||||
|
||||
// The name to use in the Server HTTP field
|
||||
std::string server_;
|
||||
|
||||
public:
|
||||
/** Constructor
|
||||
|
||||
@param root A path with files to serve. A GET request
|
||||
for "/" will try to deliver the file "/index.html".
|
||||
|
||||
@param The string to use in the Server HTTP field.
|
||||
*/
|
||||
explicit
|
||||
file_service(
|
||||
boost::filesystem::path const& root,
|
||||
beast::string_view server)
|
||||
: root_(root)
|
||||
, server_(server)
|
||||
{
|
||||
}
|
||||
|
||||
/** Initialize the service.
|
||||
|
||||
This provides an opportunity for the service to perform
|
||||
initialization which may fail, while reporting an error
|
||||
code instead of throwing an exception from the constructor.
|
||||
|
||||
@note This is needed for to meet the requirements for @b Service
|
||||
*/
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
// This is required by the error_code specification
|
||||
//
|
||||
ec = {};
|
||||
}
|
||||
|
||||
/** Process a request.
|
||||
|
||||
|
||||
@note This is needed for to meet the requirements for @b Service
|
||||
*/
|
||||
template<
|
||||
class Stream,
|
||||
class Body, class Fields,
|
||||
class Send>
|
||||
bool
|
||||
respond(
|
||||
Stream&&,
|
||||
endpoint_type const& ep,
|
||||
beast::http::request<Body, Fields>&& req,
|
||||
Send const& send) const
|
||||
{
|
||||
// Check the method and take action
|
||||
switch(req.method())
|
||||
{
|
||||
case beast::http::verb::get:
|
||||
{
|
||||
// For GET requests we deliver the actual file
|
||||
boost::filesystem::path rel_path(req.target().to_string());
|
||||
|
||||
// Give them the root web page if the target is "/"
|
||||
if(rel_path == "/")
|
||||
rel_path = "/index.html";
|
||||
|
||||
// Calculate full path from root
|
||||
boost::filesystem::path full_path = root_ / rel_path;
|
||||
|
||||
// Make sure the file is there
|
||||
if(boost::filesystem::exists(full_path))
|
||||
{
|
||||
// Send the file
|
||||
send(get(req, full_path));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send a Not Found result
|
||||
send(not_found(req, rel_path));
|
||||
}
|
||||
|
||||
// Indicate that we handled the request
|
||||
return true;
|
||||
}
|
||||
|
||||
case beast::http::verb::head:
|
||||
{
|
||||
// We are just going to give them the headers they
|
||||
// would otherwise get, but without the body.
|
||||
boost::filesystem::path rel_path(req.target().to_string());
|
||||
if(rel_path == "/")
|
||||
rel_path = "/index.html";
|
||||
|
||||
// Calculate full path from root
|
||||
boost::filesystem::path full_path = root_ / rel_path;
|
||||
|
||||
// Make sure the file is there
|
||||
if(! boost::filesystem::exists(full_path))
|
||||
{
|
||||
// Send a HEAD response
|
||||
send(head(req, full_path));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send a Not Found result
|
||||
send(not_found(req, rel_path));
|
||||
}
|
||||
|
||||
// Indicate that we handled the request
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// We didn't handle this request, so return false to
|
||||
// inform the service list to try the next service.
|
||||
//
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
// Return a reasonable mime type based on the extension of a file.
|
||||
//
|
||||
beast::string_view
|
||||
mime_type(boost::filesystem::path const& path) const
|
||||
{
|
||||
using beast::iequals;
|
||||
auto const ext = path.extension().string();
|
||||
if(iequals(ext, ".txt")) return "text/plain";
|
||||
if(iequals(ext, ".htm")) return "text/html";
|
||||
if(iequals(ext, ".html")) return "text/html";
|
||||
if(iequals(ext, ".php")) return "text/html";
|
||||
if(iequals(ext, ".css")) return "text/css";
|
||||
if(iequals(ext, ".js")) return "application/javascript";
|
||||
if(iequals(ext, ".json")) return "application/json";
|
||||
if(iequals(ext, ".xml")) return "application/xml";
|
||||
if(iequals(ext, ".swf")) return "application/x-shockwave-flash";
|
||||
if(iequals(ext, ".flv")) return "video/x-flv";
|
||||
if(iequals(ext, ".png")) return "image/png";
|
||||
if(iequals(ext, ".jpe")) return "image/jpeg";
|
||||
if(iequals(ext, ".jpeg")) return "image/jpeg";
|
||||
if(iequals(ext, ".jpg")) return "image/jpeg";
|
||||
if(iequals(ext, ".gif")) return "image/gif";
|
||||
if(iequals(ext, ".bmp")) return "image/bmp";
|
||||
if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon";
|
||||
if(iequals(ext, ".tiff")) return "image/tiff";
|
||||
if(iequals(ext, ".tif")) return "image/tiff";
|
||||
if(iequals(ext, ".svg")) return "image/svg+xml";
|
||||
if(iequals(ext, ".svgz")) return "image/svg+xml";
|
||||
return "application/text";
|
||||
}
|
||||
|
||||
// Return an HTTP Not Found response
|
||||
//
|
||||
template<class Body, class Fields>
|
||||
beast::http::response<beast::http::string_body>
|
||||
not_found(beast::http::request<Body, Fields> const& req,
|
||||
boost::filesystem::path const& rel_path) const
|
||||
{
|
||||
beast::http::response<beast::http::string_body> res;
|
||||
res.version = req.version;
|
||||
res.result(beast::http::status::not_found);
|
||||
res.set(beast::http::field::server, server_);
|
||||
res.set(beast::http::field::content_type, "text/html");
|
||||
res.body = "The file was not found"; // VFALCO append rel_path
|
||||
res.prepare();
|
||||
return res;
|
||||
}
|
||||
|
||||
// Return a file response to an HTTP GET request
|
||||
//
|
||||
template<class Body, class Fields>
|
||||
beast::http::response<file_body>
|
||||
get(beast::http::request<Body, Fields> const& req,
|
||||
boost::filesystem::path const& full_path) const
|
||||
{
|
||||
beast::http::response<file_body> res;
|
||||
res.version = req.version;
|
||||
res.result(beast::http::status::ok);
|
||||
res.set(beast::http::field::server, server_);
|
||||
res.set(beast::http::field::content_type, mime_type(full_path));
|
||||
res.body = full_path;
|
||||
res.prepare();
|
||||
return res;
|
||||
}
|
||||
|
||||
// Return a response to an HTTP HEAD request
|
||||
//
|
||||
template<class Body, class Fields>
|
||||
beast::http::response<beast::http::empty_body>
|
||||
head(beast::http::request<Body, Fields> const& req,
|
||||
boost::filesystem::path const& full_path) const
|
||||
{
|
||||
beast::http::response<beast::http::empty_body> res;
|
||||
res.version = req.version;
|
||||
res.result(beast::http::status::ok);
|
||||
res.set(beast::http::field::server, server_);
|
||||
res.set(beast::http::field::content_type, mime_type(full_path));
|
||||
|
||||
// Set the Content-Length field manually. We don't have a body,
|
||||
// but this is a response to a HEAD request so we include the
|
||||
// content length anyway.
|
||||
//
|
||||
res.set(beast::http::field::content_length, file_body::size(full_path));
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
} // framework
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user