Hello libart-seafire-resources
All checks were successful
on-push / build-and-test (push) Successful in 59s

This commit is contained in:
2025-10-18 00:45:23 +02:00
commit 977d921a3c
41 changed files with 1704 additions and 0 deletions

9
art/seafire/resources/.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
# Generated version header.
#
version.hxx
# Unit test executables and Testscript output directories
# (can be symlinks).
#
*.test
test-*.test

View File

@@ -0,0 +1,67 @@
intf_libs = # Interface dependencies.
impl_libs = # Implementation dependencies.
import intf_libs =+ libart-seafire-common%lib{art-seafire-common}
import intf_libs =+ libart-seafire-protocol%lib{art-seafire-protocol}
import intf_libs =+ libart-seafire-server%lib{art-seafire-server}
import intf_libs =+ libart-seafire-representation%lib{art-seafire-representation}
./: lib{art-seafire-resources}: libul{art-seafire-resources}
libul{art-seafire-resources}: {hxx ixx txx cxx}{** -**.test... -version} \
{hxx }{ version}
libul{art-seafire-resources}: $impl_libs $intf_libs
# Unit tests.
#
exe{*.test}:
{
test = true
install = false
}
for t: cxx{**.test...}
{
d = $directory($t)
n = $name($t)...
./: $d/exe{$n}: $t $d/{hxx ixx txx}{+$n} $d/testscript{+$n}
$d/exe{$n}: libul{art-seafire-resources}: bin.whole = false
}
hxx{version}: in{version} $src_root/manifest
{
dist = true
clean = ($src_root != $out_root)
}
# Build options.
#
cxx.poptions =+ "-I$out_root" "-I$src_root"
# Export options.
#
lib{art-seafire-resources}:
{
cxx.export.poptions = "-I$out_root" "-I$src_root"
cxx.export.libs = $intf_libs
}
# For pre-releases use the complete version to make sure they cannot
# be used in place of another pre-release or the final version. See
# the version module for details on the version.* variable values.
#
if $version.pre_release
lib{art-seafire-resources}: bin.lib.version = "-$version.project_id"
else
lib{art-seafire-resources}: bin.lib.version = "-$version.major.$version.minor"
# Install into the art/seafire/resources/ subdirectory of, say, /usr/include/
# recreating subdirectories.
#
{hxx ixx txx}{*}:
{
install = include/art/seafire/resources/
install.subdirs = true
}

View File

@@ -0,0 +1,33 @@
#ifndef art__seafire__resources__concepts_hxx_
#define art__seafire__resources__concepts_hxx_
#include <art/seafire/resources/traits.hxx>
namespace art::seafire::resources
{
// resources.
//
template<typename R>
concept GettableResource = traits::is_gettable_resource_v<R>;
template<typename R>
concept UpdatableResource = traits::is_updatable_resource_v<R>;
template<typename R>
concept CreatableResource = traits::is_creatable_resource_v<R>;
template<typename R>
concept ErasableResource = traits::is_erasable_resource_v<R>;
template<typename R>
concept Resource
= traits::is_gettable_resource_v<R>
|| traits::is_updatable_resource_v<R>
|| traits::is_creatable_resource_v<R>
|| traits::is_erasable_resource_v<R>;
} // namespace art::seafire::resources
#endif

View File

@@ -0,0 +1,32 @@
#ifndef art__seafire__resources__handle_create_hxx_
#define art__seafire__resources__handle_create_hxx_
#include <art/seafire/common/invoke.hxx>
#include <art/seafire/protocol/media-type.hxx>
#include <art/seafire/protocol/rfc7231/content-type.hxx>
#include <art/seafire/protocol/rfc7231/location.hxx>
#include <art/seafire/protocol/rfc7232/etag.hxx>
#include <art/seafire/protocol/rfc7232/last-modified.hxx>
#include <art/seafire/representation/representation.hxx>
#include <art/seafire/resources/concepts.hxx>
#include <art/seafire/resources/handle-get.hxx>
#include <art/seafire/resources/metadata.hxx>
#include <art/seafire/resources/preconditions.hxx>
#include <art/seafire/server/request.hxx>
#include <art/seafire/server/response.hxx>
namespace art::seafire::resources
{
template<CreatableResource R>
void
handle_create(server::request_t& req,
server::response_t& res,
R const& resource,
std::optional<protocol::media_type_t> const& accepted_type);
} // namespace art::seafire::resources
#include <art/seafire/resources/handle-create.txx>
#endif

View File

@@ -0,0 +1,40 @@
namespace art::seafire::resources
{
template<CreatableResource R>
void
handle_create(server::request_t& req,
server::response_t& res,
R const& resource,
std::optional<protocol::media_type_t> const& accepted_type)
{
using resource_traits = traits::resource_traits<std::decay_t<R>>;
using input_representation_type = typename resource_traits::create_input_representation_type;
using t = traits::resource_traits<R>;
if constexpr (std::is_same_v<void, typename t::create_result_type>) {
auto content_type = get<protocol::rfc7231::content_type_t>(req);
auto input = input_representation_type::read_from(content_type, req.content());
common::invoke(resource, req, &R::create, input);
res.send(204);
}
else if constexpr (representation::traits::is_variant_input_representation_v<input_representation_type>) {
res.send(server::common_error_t::not_implemented);
}
else if constexpr (representation::Representation<typename t::create_result_type>) {
auto content_type = get<protocol::rfc7231::content_type_t>(req);
auto input = input_representation_type::read_from(content_type, req.content());
auto result = common::invoke(resource, req, &R::create, input);
auto selected_rep = representation::select(result, accepted_type);
representation::send(req, res, 201, selected_rep, true);
}
else {
auto content_type = get<protocol::rfc7231::content_type_t>(req);
auto input = input_representation_type::read_from(content_type, req.content());
auto result = common::invoke(resource, req, &R::create, input);
handle_get(req, res, result, accepted_type, get_kind_t::created_resource);
}
}
} // namespace art::seafire::resources

View File

@@ -0,0 +1,34 @@
#ifndef art__seafire__resources__handle_erase_hxx_
#define art__seafire__resources__handle_erase_hxx_
#include <art/seafire/common/invoke.hxx>
#include <art/seafire/protocol/media-type.hxx>
#include <art/seafire/protocol/rfc7231/content-type.hxx>
#include <art/seafire/protocol/rfc7231/location.hxx>
#include <art/seafire/protocol/rfc7232/etag.hxx>
#include <art/seafire/protocol/rfc7232/last-modified.hxx>
#include <art/seafire/representation/representation.hxx>
#include <art/seafire/representation/select.hxx>
#include <art/seafire/representation/send.hxx>
#include <art/seafire/resources/concepts.hxx>
#include <art/seafire/resources/metadata.hxx>
#include <art/seafire/resources/preconditions.hxx>
#include <art/seafire/server/transaction.hxx>
#include <optional>
namespace art::seafire::resources
{
template<ErasableResource R>
void
handle_erase(server::request_t&,
server::response_t&,
R const&,
std::optional<protocol::media_type_t> const&);
} // namespace art::seafire::resources
#include <art/seafire/resources/handle-erase.txx>
#endif

View File

@@ -0,0 +1,25 @@
namespace art::seafire::resources
{
template<ErasableResource R>
void
handle_erase(server::request_t& req,
server::response_t& res,
R const& resource,
std::optional<protocol::media_type_t> const& accepted_type)
{
using t = traits::resource_traits<R>;
if constexpr (representation::Representation<typename t::erase_result_type>) {
auto result = common::invoke(resource, req, &R::erase);
auto selected_rep = representation::select(result, accepted_type);
representation::send(req, res, 200, selected_rep, true);
}
else {
common::invoke(resource, req, &R::erase);
res.send(204);
}
}
} // namespace art::seafire::resources

View File

@@ -0,0 +1,42 @@
#ifndef art__seafire__resources__handle_get_hxx_
#define art__seafire__resources__handle_get_hxx_
#include <art/seafire/common/invoke.hxx>
#include <art/seafire/protocol/media-type.hxx>
#include <art/seafire/protocol/rfc7231/content-type.hxx>
#include <art/seafire/protocol/rfc7231/location.hxx>
#include <art/seafire/protocol/rfc7232/etag.hxx>
#include <art/seafire/protocol/rfc7232/last-modified.hxx>
#include <art/seafire/representation/representation.hxx>
#include <art/seafire/representation/select.hxx>
#include <art/seafire/representation/send.hxx>
#include <art/seafire/resources/concepts.hxx>
#include <art/seafire/resources/metadata.hxx>
#include <art/seafire/resources/preconditions.hxx>
#include <art/seafire/server/transaction.hxx>
#include <optional>
namespace art::seafire::resources
{
enum class get_kind_t {
get,
head,
created_resource,
updated_resource
};
template<GettableResource R>
void
handle_get(server::request_t& req,
server::response_t& res,
R const& resource,
std::optional<protocol::media_type_t> const& accepted_type,
get_kind_t kind);
} // namespace art::seafire::resources
#include <art/seafire/resources/handle-get.txx>
#endif

View File

@@ -0,0 +1,41 @@
namespace art::seafire::resources
{
template<GettableResource R>
void
handle_get(server::request_t& req,
server::response_t& res,
R const& resource,
std::optional<protocol::media_type_t> const& accepted_type,
get_kind_t kind)
{
auto rep = common::invoke(resource, req, &R::get);
auto selected_rep = representation::select(rep, accepted_type);
if (!check_preconditions(req, res, selected_rep))
return;
namespace rfc7231 = protocol::rfc7231;
namespace rfc7232 = protocol::rfc7232;
if (auto opt_val = get_etag(resource); opt_val)
set<rfc7232::etag_t>(res, *opt_val);
if (auto opt_val = get_last_modified(resource); opt_val)
set<rfc7232::last_modified_t>(res, *opt_val);
if (kind == get_kind_t::created_resource || kind == get_kind_t::updated_resource) {
if (auto opt_val = get_location(resource); opt_val)
set<rfc7231::location_t>(res, *opt_val);
}
protocol::status_code_t status{200};
if (kind == get_kind_t::created_resource)
status = 201;
bool const send_content = kind != get_kind_t::head;
representation::send(req, res, status, selected_rep, send_content);
}
} // namespace art::seafire::resources

View File

@@ -0,0 +1,34 @@
#ifndef art__seafire__resources__handle_update_hxx_
#define art__seafire__resources__handle_update_hxx_
#include <art/seafire/common/invoke.hxx>
#include <art/seafire/protocol/media-type.hxx>
#include <art/seafire/protocol/rfc7231/content-type.hxx>
#include <art/seafire/protocol/rfc7231/location.hxx>
#include <art/seafire/protocol/rfc7232/etag.hxx>
#include <art/seafire/protocol/rfc7232/last-modified.hxx>
#include <art/seafire/representation/representation.hxx>
#include <art/seafire/resources/concepts.hxx>
#include <art/seafire/resources/handle-get.hxx>
#include <art/seafire/resources/metadata.hxx>
#include <art/seafire/resources/preconditions.hxx>
#include <art/seafire/server/request.hxx>
#include <art/seafire/server/response.hxx>
#include <optional>
namespace art::seafire::resources
{
template<UpdatableResource R>
void
handle_update(server::request_t& req,
server::response_t& res,
R const& resource,
std::optional<protocol::media_type_t> const& accepted_type);
} // namespace art::seafire::resources
#include <art/seafire/resources/handle-update.txx>
#endif

View File

@@ -0,0 +1,64 @@
namespace art::seafire::resources
{
template<UpdatableResource R>
void
handle_update(server::request_t& req,
server::response_t& res,
R const& resource,
std::optional<protocol::media_type_t> const& accepted_type)
{
using resource_traits = traits::resource_traits<std::decay_t<R>>;
using input_representation_type = typename resource_traits::update_input_representation_type;
if constexpr (std::is_same_v<void, input_representation_type>) {
using t = traits::resource_traits<R>;
if constexpr (GettableResource<typename t::update_result_type>) {
auto result = common::invoke(resource, req, &R::update);
handle_get(req, res, result, accepted_type, get_kind_t::updated_resource);
}
else if constexpr (representation::Representation<typename t::update_result_type>) {
auto result = common::invoke(resource, req, &R::update);
auto selected_rep = representation::select(result, accepted_type);
representation::send(req, res, 200, selected_rep, true);
}
else {
common::invoke(resource, req, &R::update);
res.send(204);
}
}
else {
auto content_type = get<protocol::rfc7231::content_type_t>(req);
if (!content_type) {
res.send(400);
return;
}
if (!input_representation_type::can_accept_input(*content_type)) {
res.send(400);
return;
}
auto input = input_representation_type::read_from(content_type, req.content());
using t = traits::resource_traits<R>;
if constexpr (GettableResource<typename t::update_result_type>) {
auto result = common::invoke(resource, req, &R::update, input);
handle_get(req, res, result, accepted_type, get_kind_t::updated_resource);
}
else if constexpr (representation::Representation<typename t::update_result_type>) {
auto result = common::invoke(resource, req, &R::update, input);
auto selected_rep = representation::select(result, accepted_type);
representation::send(req, res, 200, selected_rep, true);
}
else {
common::invoke(resource, req, &R::update, input);
res.send(204);
}
}
}
} // namespace art::seafire::resources

View File

@@ -0,0 +1,30 @@
#ifndef art__seafire__resources__handle_hxx_
#define art__seafire__resources__handle_hxx_
#include <art/seafire/protocol/rfc7231/allow.hxx>
#include <art/seafire/resources/concepts.hxx>
#include <art/seafire/resources/handle-create.hxx>
#include <art/seafire/resources/handle-erase.hxx>
#include <art/seafire/resources/handle-get.hxx>
#include <art/seafire/resources/handle-update.hxx>
#include <art/seafire/resources/negotiate.hxx>
#include <art/seafire/resources/traits.hxx>
#include <art/seafire/server/request.hxx>
#include <art/seafire/server/response.hxx>
#include <sstream>
#include <type_traits>
namespace art::seafire::resources
{
void
handle(server::request_t& req,
server::response_t& res,
Resource auto const& r);
} // namespace art::seafire::resources
#include <art/seafire/resources/handle.txx>
#endif

View File

@@ -0,0 +1,157 @@
namespace art::seafire::resources
{
void
handle(server::request_t& req,
server::response_t& res,
Resource auto const& r)
{
using resource_traits = traits::resource_traits<std::decay_t<decltype(r)>>;
auto const not_allowed = [&res]
{
set<protocol::rfc7231::allow_t>(res, resource_traits::allowed_methods());
res.send(server::common_error_t::method_not_allowed);
};
auto const& method = req.get_message().method();
// GET
//
if (method == "GET" || method == "HEAD") {
if constexpr (resource_traits::is_gettable) {
negotiate<typename resource_traits::get_representation_type>(
req,
res,
[&req, &res, &r](std::optional<protocol::media_type_t> const& accepted_type)
{
handle_get(
req,
res,
r,
accepted_type,
req.get_message().method() == "HEAD" ? get_kind_t::head : get_kind_t::get
);
}
);
return;
}
else {
not_allowed();
}
}
// PUT (update)
//
else if (method == "PUT") {
if constexpr (resource_traits::is_updatable) {
if constexpr (std::is_same_v<void, typename resource_traits::update_result_type>) {
handle_update(req, res, r, std::nullopt);
}
else if constexpr (representation::Representation<typename resource_traits::update_result_type>) {
negotiate<typename resource_traits::update_result_type>(
req,
res,
[&req, &res, &r](std::optional<protocol::media_type_t> const& accepted_type)
{
handle_update(req, res, r, accepted_type);
}
);
}
else {
negotiate<typename traits::resource_traits<typename resource_traits::update_result_type>::get_representation_type>(
req,
res,
[&req, &res, &r](std::optional<protocol::media_type_t> const& accepted_type)
{
handle_update(req, res, r, accepted_type);
}
);
}
}
else {
not_allowed();
}
}
// POST (create)
//
else if (method == "POST") {
if constexpr (resource_traits::is_creatable) {
if constexpr (std::is_same_v<void, typename resource_traits::update_result_type>) {
handle_create(req, res, r, std::nullopt);
}
else if constexpr (representation::Representation<typename resource_traits::create_result_type>) {
negotiate<typename resource_traits::create_result_type>(
req,
res,
[&req, &res, &r](std::optional<protocol::media_type_t> const& accepted_type)
{
handle_create(req, res, r, accepted_type);
}
);
}
else {
using one = typename resource_traits::create_result_type;
using two = typename traits::resource_traits<one>::get_representation_type;
//using resource_traits = traits::resource_traits<
negotiate<two>(
req,
res,
[&req, &res, &r](std::optional<protocol::media_type_t> const& accepted_type)
{
handle_create(req, res, r, accepted_type);
}
);
}
}
else {
not_allowed();
}
}
// DELETE
//
else if (method == "DELETE") {
if constexpr (resource_traits::is_erasable) {
if constexpr (std::is_same_v<void, typename resource_traits::erase_result_type>) {
handle_erase(req, res, r, std::nullopt);
}
else if constexpr (representation::Representation<typename resource_traits::erase_result_type>) {
negotiate<typename resource_traits::erase_result_type>(
req,
res,
[&req, &res, &r](std::optional<protocol::media_type_t> const& accepted_type)
{
handle_erase(req, res, r, accepted_type);
}
);
}
else {
using one = typename resource_traits::erase_result_type;
using two = typename traits::resource_traits<one>::get_representation_type;
//using resource_traits = traits::resource_traits<
negotiate<two>(
req,
res,
[&req, &res, &r](std::optional<protocol::media_type_t> const& accepted_type)
{
handle_erase(req, res, r, accepted_type);
}
);
}
}
else {
not_allowed();
}
}
// method not implemented.
//
else {
res.send(server::common_error_t::not_implemented);
}
}
} // namespace art::seafire::resources

View File

@@ -0,0 +1,104 @@
#ifndef art__seafire__resources__metadata_hxx_
#define art__seafire__resources__metadata_hxx_
#include <art/seafire/protocol/media-type.hxx>
#include <art/seafire/protocol/rfc7232/entity-tag.hxx>
#include <art/seafire/resources/concepts.hxx>
#include <art/seafire/resources/traits.hxx>
#include <art/seafire/representation/concepts.hxx>
#include <art/seafire/representation/representation.hxx>
#include <art/seafire/representation/traits.hxx>
#include <art/uri/uri.hxx>
#include <chrono>
#include <optional>
namespace art::seafire::resources
{
// location
//
template<Resource R>
std::optional<uri::uri_t>
get_location(R const& res)
{
using local_traits = traits::resource_traits<R>;
if constexpr (local_traits::has_location)
return res.location();
return std::nullopt;
}
// etag
//
template<representation::BasicRepresentation BR>
std::optional<protocol::rfc7232::entity_tag_t>
get_etag(BR const& rep)
{
using local_traits = representation::traits::representation_traits<BR>;
if constexpr (local_traits::has_entity_tag_t)
return rep.etag();
return std::nullopt;
}
inline
std::optional<protocol::rfc7232::entity_tag_t>
get_etag(representation::representation_t const& rep)
{
return rep.etag();
}
template<Resource R>
std::optional<protocol::rfc7232::entity_tag_t>
get_etag(R const& res)
{
using local_traits = traits::resource_traits<R>;
if constexpr (local_traits::has_entity_tag)
return res.etag();
return std::nullopt;
}
// last-modified
//
template<representation::BasicRepresentation BR>
std::optional<std::chrono::system_clock::time_point>
get_last_modified(BR const& rep)
{
using local_traits = representation::traits::representation_traits<BR>;
if constexpr (local_traits::has_last_modified)
return rep.last_modified();
return std::nullopt;
}
inline
std::optional<std::chrono::system_clock::time_point>
get_last_modified(representation::representation_t const& rep)
{
return rep.last_modified();
}
template<Resource R>
std::optional<std::chrono::system_clock::time_point>
get_last_modified(R const& res)
{
using local_traits = traits::resource_traits<R>;
if constexpr (local_traits::has_last_modified)
return res.last_modified();
return std::nullopt;
}
} // namespace art::seafire::resources
#endif

View File

@@ -0,0 +1,28 @@
#ifndef art__seafire__resources__negotiate_hxx_
#define art__seafire__resources__negotiate_hxx_
#include <art/seafire/protocol/media-type.hxx>
#include <art/seafire/protocol/rfc7231/accept.hxx>
#include <art/seafire/representation/concepts.hxx>
#include <art/seafire/representation/traits.hxx>
#include <art/seafire/resources/concepts.hxx>
#include <art/seafire/resources/traits.hxx>
#include <art/seafire/server/request.hxx>
#include <art/seafire/server/response.hxx>
#include <optional>
namespace art::seafire::resources
{
// fixme: implement concept for handler.
template<representation::Representation R, typename Handler>
void
negotiate(server::request_t&, server::response_t&, Handler&&);
} // namespace art::seafire::resources
#include <art/seafire/resources/negotiate.txx>
#endif

View File

@@ -0,0 +1,31 @@
namespace art::seafire::resources
{
template<representation::Representation R, typename Handler>
void
negotiate(server::request_t& req, server::response_t& res, Handler&& handler)
{
using representation_traits = representation::traits::representation_traits<R>;
if constexpr (representation_traits::is_content_negotiable) {
using protocol::rfc7231::accept_t;
if (auto opt_accept = get<accept_t>(req); opt_accept) {
auto const accept = *opt_accept;
for (auto const& accepted_type : accept) {
if (R::is_accepted(accepted_type)) {
handler(accepted_type);
return;
}
}
res.send(server::common_error_t::not_acceptable);
return;
}
}
handler(std::nullopt);
}
} // namespace art::seafire::resources

View File

@@ -0,0 +1,67 @@
#ifndef art__seafire__resources__preconditions_hxx_
#define art__seafire__resources__preconditions_hxx_
#include <art/seafire/protocol/rfc7232/if-match.hxx>
#include <art/seafire/protocol/rfc7232/if-modified-since.hxx>
#include <art/seafire/protocol/rfc7232/if-none-match.hxx>
#include <art/seafire/protocol/rfc7232/if-unmodified-since.hxx>
#include <art/seafire/resources/concepts.hxx>
#include <art/seafire/resources/traits.hxx>
#include <art/seafire/server/request.hxx>
#include <art/seafire/server/response.hxx>
#include <art/seafire/representation/concepts.hxx>
#include <art/seafire/representation/traits.hxx>
#include <chrono>
namespace art::seafire::resources
{
bool
check_preconditions(
server::request_t& req,
server::response_t& res,
representation::BasicRepresentation auto const& r);
// If-Match
//
bool
check_if_match(
server::request_t& req,
server::response_t& res,
representation::BasicRepresentation auto const& r,
protocol::rfc7232::if_match_t const& if_match);
// If-Unmodified-Since
//
bool
check_if_unmodified_since(
server::request_t& req,
server::response_t& res,
representation::BasicRepresentation auto const& representation,
std::chrono::system_clock::time_point const& if_unmodified_since);
// If-None-Match
//
bool
check_if_none_match(
server::request_t& req,
server::response_t& res,
representation::BasicRepresentation auto const& r,
protocol::rfc7232::if_none_match_t const& if_none_match);
// If-Modified-Since
//
bool
check_if_modified_since(
server::request_t& req,
server::response_t& res,
representation::BasicRepresentation auto const& r,
std::chrono::system_clock::time_point const& if_modified_since);
} // namespace art::seafire::resources
#include <art/seafire/resources/preconditions.txx>
#endif

View File

@@ -0,0 +1,151 @@
namespace art::seafire::resources
{
bool
check_preconditions(
server::request_t& req,
server::response_t& res,
representation::BasicRepresentation auto const& r)
{
namespace rfc7232 = protocol::rfc7232;
using representation_traits = representation::traits::representation_traits<decltype(r)>;
if (auto if_match = get<rfc7232::if_match_t>(req); if_match) {
if constexpr (representation_traits::has_entity_tag) {
bool match = check_if_match(req, res, r, *if_match);
if (!match) {
res.send(server::common_error_t::precondition_failed);
return false;
}
}
else {
res.send(server::common_error_t::precondition_failed);
return false;
}
}
else if (auto if_unmodified_since = get<rfc7232::if_unmodified_since_t>(req); if_unmodified_since) {
bool match = check_if_unmodified_since(req, res, r, *if_unmodified_since);
if (!match) {
res.send(server::common_error_t::precondition_failed);
return false;
}
}
if (auto if_none_match = get<rfc7232::if_none_match_t>(req); if_none_match) {
bool match = false;
if (!match) {
if (req.get_message().method() == "GET" || req.get_message().method() == "HEAD")
res.send(server::common_error_t::not_modified);
else
res.send(server::common_error_t::precondition_failed);
return false;
}
}
else if (auto if_modified_since = get<rfc7232::if_modified_since_t>(req); if_modified_since) {
bool match = false;
if (!match) {
res.send(server::common_error_t::precondition_failed);
return false;
}
}
return true;
}
// If-Match
//
bool
check_if_match(
server::request_t& req,
server::response_t& res,
representation::BasicRepresentation auto const& r,
protocol::rfc7232::if_match_t const& if_match)
{
using representation_traits = representation::traits::representation_traits<std::decay_t<decltype(r)>>;
if constexpr (representation_traits::has_entity_tag) {
auto etag = [&r]
{
if constexpr (common::traits::is_optional_v<decltype(r.etag())>) {
return r.etag();
}
else {
return std::optional<protocol::rfc7232::entity_tag_t>{r.etag()};
}
}();
if (!etag)
return false;
if (if_match.is_anything())
return true;
for (auto const& j : if_match.tags()) {
if (strong_compare(*etag, j))
return true;
}
}
return false;
}
// If-Unmodified-Since
//
bool
check_if_unmodified_since(
server::request_t& req,
server::response_t& res,
representation::BasicRepresentation auto const& r,
std::chrono::system_clock::time_point const& if_unmodified_since)
{
using representation_traits = representation::traits::representation_traits<std::decay_t<decltype(r)>>;
if constexpr (representation_traits::has_last_modified) {
auto last_modified = r.last_modified();
if (last_modified <= if_unmodified_since)
return true;
}
return false;
}
// If-None-Match
//
bool
check_if_none_match(
server::request_t& req,
server::response_t& res,
representation::BasicRepresentation auto const& r,
protocol::rfc7232::if_none_match_t const& if_none_match)
{
return false;
}
// If-Modified-Since
//
bool
check_if_modified_since(
server::request_t& req,
server::response_t& res,
representation::BasicRepresentation auto const& r,
std::chrono::system_clock::time_point const& if_modified_since)
{
using representation_traits = representation::traits::representation_traits<std::decay_t<decltype(r)>>;
if constexpr (representation_traits::has_last_modified) {
auto last_modified = r.last_modified();
if (last_modified >= if_modified_since)
return true;
}
return false;
}
} // namespace art::seafire::resources

View File

@@ -0,0 +1,66 @@
#ifndef art__seafire__resources__resource_handler_hxx_
#define art__seafire__resources__resource_handler_hxx_
#include <art/seafire/common/invoke.hxx>
#include <art/seafire/protocol/media-type.hxx>
#include <art/seafire/protocol/rfc7231/accept.hxx>
#include <art/seafire/protocol/rfc7231/content-type.hxx>
#include <art/seafire/protocol/rfc7232/entity-tag.hxx>
#include <art/seafire/protocol/rfc7232/etag.hxx>
#include <art/seafire/protocol/rfc7232/last-modified.hxx>
#include <art/seafire/resources/concepts.hxx>
#include <art/seafire/resources/handle.hxx>
#include <art/seafire/resources/traits.hxx>
#include <art/seafire/server/request.hxx>
#include <art/seafire/server/response.hxx>
#include <optional>
#include <string>
#include <tuple>
#include <vector>
namespace art::seafire::resources
{
template<Resource R,
typename F, // TODO: Implement factory concept
typename... FactoryArgs>
class resource_handler_t {
public:
using resource_type = R;
using factory_type = F;
explicit
resource_handler_t(factory_type factory, FactoryArgs&&... args);
factory_type const&
factory() const
{
return factory_;
}
void
on_request(server::request_t& req, server::response_t& res) const;
void
operator()(server::request_t& req, server::response_t& res) const
{
on_request(req, res);
}
private:
factory_type factory_;
std::tuple<FactoryArgs...> factory_args_;
};
template<Resource R,
typename F,
typename... FactoryArgs,
typename H = resource_handler_t<R, F, FactoryArgs...>>
H use_resource(F factory, FactoryArgs&&... args);
} // namespace art::seafire::resources
#include <art/seafire/resources/resource-handler.txx>
#endif

View File

@@ -0,0 +1,36 @@
namespace art::seafire::resources
{
template<Resource R, typename F, typename... FactoryArgs >
resource_handler_t<R, F, FactoryArgs...>::
resource_handler_t(factory_type factory, FactoryArgs&&... args)
: factory_{std::move(factory)},
factory_args_{std::forward<FactoryArgs>(args)...}
{}
template< Resource R, typename F, typename... FactoryArgs >
void
resource_handler_t< R, F, FactoryArgs... >::
on_request(server::request_t& req,
server::response_t& res) const
{
auto factory_invoker = [f = factory(), &req, &res](auto... args)
{
return common::invoke(req, &F::template factory<resource_type>::make, f, req, res, std::forward<decltype(args)>(args)...);
};
auto r = std::apply(factory_invoker, factory_args_);
handle(req, res, r);
}
template<Resource R,
typename F,
typename... FactoryArgs,
typename H>
H
use_resource(F factory, FactoryArgs&&... args)
{
return H{std::move(factory), std::forward<FactoryArgs>(args)...};
}
} // namespace art::seafire::resources

View File

@@ -0,0 +1,339 @@
#ifndef art__seafire__resources__traits_hxx_
#define art__seafire__resources__traits_hxx_
#include <art/seafire/common/traits.hxx>
#include <art/seafire/protocol/media-type.hxx>
#include <art/seafire/protocol/rfc7232/entity-tag.hxx>
#include <art/seafire/protocol/token.hxx>
#include <art/seafire/representation/traits.hxx>
#include <art/uri/uri.hxx>
#include <chrono>
#include <cstdint>
#include <iostream>
#include <optional>
#include <tuple>
#include <type_traits>
#include <variant>
#include <vector>
namespace art::seafire::resources::traits
{
// =============================================================================
// resources.
//
// is_gettable_resource<R>
//
template<typename, typename = std::void_t<>>
struct is_gettable_resource : std::false_type {};
template<typename R>
struct is_gettable_resource<
R,
std::void_t<
decltype(&R::get)
>
> : std::bool_constant<
std::is_same_v<
void,
typename common::traits::function_traits<decltype(&R::get)>::return_type
>
||
representation::traits::is_representation_v<
typename common::traits::function_traits<decltype(&R::get)>::return_type
>
> {
};
template<typename R>
inline constexpr bool is_gettable_resource_v{is_gettable_resource<R>::value};
template<typename R, bool = is_gettable_resource_v<R>>
struct get_traits {
using result_type = void;
};
template<typename R>
struct get_traits<R, true> {
using function_traits = common::traits::function_traits<decltype(&R::get)>;
using result_type = common::traits::remove_optional_t<typename function_traits::return_type>;
};
// is_updatable_resource<R>
//
template<typename, typename = std::void_t<>>
struct is_updatable_resource : std::false_type {};
template<typename R>
struct is_updatable_resource<
R,
std::void_t<
decltype(&R::update)
>
> : std::bool_constant<
// Check if the first arg to R::update() is an input representation.
//
(
std::is_same_v<
void,
typename common::traits::function_arg_n<decltype(&R::update), 0>::type
>
||
representation::traits::is_input_representation_v<
typename common::traits::function_arg_n<decltype(&R::update), 0>::type
>
)
&&
(
// Check if return value of R::update() is void.
std::is_same_v<
void,
typename common::traits::function_traits<decltype(&R::update)>::return_type
>
||
// Check if return value of R::update() is a gettable resource.
is_gettable_resource_v<
typename common::traits::function_traits<decltype(&R::update)>::return_type
>
||
// Check if return value of R::update() is a representation.
representation::traits::is_representation_v<
typename common::traits::function_traits<decltype(&R::update)>::return_type
>
)
> {
};
template<typename R>
inline constexpr bool is_updatable_resource_v{is_updatable_resource<R>::value};
template<typename R, bool = is_updatable_resource_v<R>>
struct update_traits {
using input_representation_type = void;
using result_type = void;
};
template<typename R>
struct update_traits<R, true> {
using function_traits = common::traits::function_traits<decltype(&R::update)>;
using input_representation_type = common::traits::function_arg_n<decltype(&R::update), 0>::type;
using result_type = typename function_traits::return_type;
};
// is_creatable_resource<R>
//
template<typename, typename = std::void_t<>>
struct is_creatable_resource : std::false_type {};
template<typename R>
struct is_creatable_resource<
R,
std::void_t<
decltype(&R::create)
>
> : std::bool_constant<
representation::traits::is_input_representation_v<
typename common::traits::function_arg_n<decltype(&R::create), 0>::type
>
&&
(
// Check if return value of R::create() is void.
std::is_same_v<
void,
typename common::traits::function_traits<decltype(&R::create)>::return_type
>
||
// Check if return value of R::create() is a gettable resource.
is_gettable_resource_v<
typename common::traits::function_traits<decltype(&R::create)>::return_type
>
||
// Check if return value of R::create() is a representation.
representation::traits::is_representation_v<
typename common::traits::function_traits<decltype(&R::create)>::return_type
>
)
> {
};
template<typename R>
inline constexpr bool is_creatable_resource_v{is_creatable_resource<R>::value};
template<typename R, bool = is_creatable_resource_v<R>>
struct create_traits {
using input_representation_type = void;
using result_type = void;
};
template<typename R>
struct create_traits<R, true> {
using function_traits = common::traits::function_traits<decltype(&R::create)>;
using input_representation_type = common::traits::function_arg_n<decltype(&R::create), 0>::type;
using result_type = typename function_traits::return_type;
};
// is_erasable_resource<R>
//
template<typename, typename = std::void_t<>>
struct is_erasable_resource : std::false_type {};
template<typename R>
struct is_erasable_resource<
R,
std::void_t<
decltype(std::declval<R const>().erase())
>
> : std::true_type{
};
template<typename R>
constexpr bool is_erasable_resource_v{is_erasable_resource<R>::value};
template<typename R, bool = is_erasable_resource_v<R>>
struct erase_traits {
using result_type = void;
};
template<typename R>
struct erase_traits<R, true> {
using function_traits = common::traits::function_traits<decltype(&R::erase)>;
using result_type = typename function_traits::return_type;
};
// is_resource
//
template<typename R>
struct is_resource : std::bool_constant<
is_gettable_resource_v<R> ||
is_updatable_resource_v<R> ||
is_creatable_resource_v<R> ||
is_erasable_resource_v<R>
> {
};
template<typename R>
constexpr bool is_resource_v{is_resource<R>::value};
// has_location
//
template<typename, typename = std::void_t<>>
struct has_location : std::false_type {};
template<typename R>
struct has_location<
R,
std::void_t<
decltype(
uri::uri_t{
std::declval<R const>().location()
}
)
>
> : std::true_type{
};
template<typename R>
constexpr bool has_location_v{has_location<R>::value};
// resource_traits
//
template<typename R>
struct resource_traits {
using resource_type = R;
// properties
//
static constexpr bool has_entity_tag{
representation::traits::has_entity_tag_v<resource_type>
};
static constexpr bool has_last_modified{
representation::traits::has_last_modified_v<resource_type>
};
static constexpr bool has_location{
has_location_v<resource_type>
};
static
protocol::tokens_t
allowed_methods()
{
protocol::tokens_t methods;
if constexpr (is_gettable) {
methods.emplace_back("GET");
methods.emplace_back("HEAD");
}
if constexpr (is_updatable)
methods.emplace_back("PUT");
if constexpr (is_creatable)
methods.emplace_back("POST");
if constexpr (is_erasable)
methods.emplace_back("DELETE");
return methods;
}
// GET
//
static constexpr bool is_gettable{
is_gettable_resource_v<resource_type>
};
using get_traits = traits::get_traits<resource_type>;
using get_representation_type = typename get_traits::result_type;
using get_representation_type_traits = representation::traits::representation_traits<
get_representation_type
>;
// UPDATE
//
static constexpr bool is_updatable{
is_updatable_resource_v<resource_type>
};
using update_traits = traits::update_traits<resource_type>;
using update_input_representation_type = typename update_traits::input_representation_type;
using update_input_representation_type_traits = representation::traits::input_representation_traits<update_input_representation_type>;
using update_result_type = typename update_traits::result_type;
// CREATE
//
static constexpr bool is_creatable{
is_creatable_resource_v<resource_type>
};
using create_traits = traits::create_traits<resource_type>;
using create_input_representation_type = typename create_traits::input_representation_type;
using create_input_representation_type_traits = representation::traits::input_representation_traits<create_input_representation_type>;
using create_result_type = typename create_traits::result_type;
// ERASE
//
static constexpr bool is_erasable{
is_erasable_resource_v<resource_type>
};
using erase_traits = traits::erase_traits<resource_type>;
using erase_result_type = typename erase_traits::result_type;
};
} // namespace art::seafire::resources::traits
#endif

View File

@@ -0,0 +1,37 @@
#ifndef art__seafire__resources__version_hxx_
#define art__seafire__resources__version_hxx_
// The numeric version format is AAAAABBBBBCCCCCDDDE where:
//
// AAAAA - major version number
// BBBBB - minor version number
// CCCCC - bugfix version number
// DDD - alpha / beta (DDD + 500) version number
// E - final (0) / snapshot (1)
//
// When DDDE is not 0, 1 is subtracted from AAAAABBBBBCCCCC. For example:
//
// Version AAAAABBBBBCCCCCDDDE
//
// 0.1.0 0000000001000000000
// 0.1.2 0000000001000020000
// 1.2.3 0000100002000030000
// 2.2.0-a.1 0000200001999990010
// 3.0.0-b.2 0000299999999995020
// 2.2.0-a.1.z 0000200001999990011
//
#define LIBART_SEAFIRE_RESOURCES_VERSION $libart_seafire_resources.version.project_number$ULL
#define LIBART_SEAFIRE_RESOURCES_VERSION_STR "$libart_seafire_resources.version.project$"
#define LIBART_SEAFIRE_RESOURCES_VERSION_ID "$libart_seafire_resources.version.project_id$"
#define LIBART_SEAFIRE_RESOURCES_VERSION_FULL "$libart_seafire_resources.version$"
#define LIBART_SEAFIRE_RESOURCES_VERSION_MAJOR $libart_seafire_resources.version.major$
#define LIBART_SEAFIRE_RESOURCES_VERSION_MINOR $libart_seafire_resources.version.minor$
#define LIBART_SEAFIRE_RESOURCES_VERSION_PATCH $libart_seafire_resources.version.patch$
#define LIBART_SEAFIRE_RESOURCES_PRE_RELEASE $libart_seafire_resources.version.pre_release$
#define LIBART_SEAFIRE_RESOURCES_SNAPSHOT_SN $libart_seafire_resources.version.snapshot_sn$ULL
#define LIBART_SEAFIRE_RESOURCES_SNAPSHOT_ID "$libart_seafire_resources.version.snapshot_id$"
#endif