You've already forked libart-seafire-routing
Hello libart-seafire-routing
All checks were successful
on-push / build-and-test (push) Successful in 1m1s
All checks were successful
on-push / build-and-test (push) Successful in 1m1s
This commit is contained in:
17
.editorconfig
Normal file
17
.editorconfig
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
indent_size = 4
|
||||||
|
max_line_length = off
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.yaml]
|
||||||
|
indent_size = 2
|
||||||
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
* text=auto
|
||||||
31
.gitea/workflows/on-push.yaml
Normal file
31
.gitea/workflows/on-push.yaml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
name: on-push
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags-ignore:
|
||||||
|
- '*'
|
||||||
|
branches:
|
||||||
|
- '**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-test:
|
||||||
|
runs-on: linux
|
||||||
|
container: code.helloryan.se/art/infra/buildenv/x86_64-fedora_42-unified:latest
|
||||||
|
volumes:
|
||||||
|
- /build
|
||||||
|
steps:
|
||||||
|
- name: Configure repository access
|
||||||
|
run: |
|
||||||
|
git config --global http.$GITHUB_SERVER_URL/.extraheader "Authorization: token ${{ secrets.ACT_RUNNER_TOKEN }}"
|
||||||
|
- name: Configure build directory
|
||||||
|
run: |
|
||||||
|
bpkg create -d /build cc config.cxx=clang++ config.cc.coptions="-Wall -Werror -Wno-unknown-pragmas"
|
||||||
|
- name: Build package
|
||||||
|
run: |
|
||||||
|
cd /build
|
||||||
|
bpkg build --yes --trust-yes $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git##$GITHUB_SHA
|
||||||
|
- name: Test package
|
||||||
|
run: |
|
||||||
|
cd /build
|
||||||
|
b test
|
||||||
|
|
||||||
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
patreon: helloryan
|
||||||
32
.gitignore
vendored
Normal file
32
.gitignore
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
.bdep/
|
||||||
|
Doxyfile
|
||||||
|
|
||||||
|
# Local default options files.
|
||||||
|
#
|
||||||
|
.build2/local/
|
||||||
|
|
||||||
|
# Compiler/linker output.
|
||||||
|
#
|
||||||
|
*.d
|
||||||
|
*.t
|
||||||
|
*.i
|
||||||
|
*.i.*
|
||||||
|
*.ii
|
||||||
|
*.ii.*
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.gcm
|
||||||
|
*.pcm
|
||||||
|
*.ifc
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
*.a
|
||||||
|
*.lib
|
||||||
|
*.exp
|
||||||
|
*.pdb
|
||||||
|
*.ilk
|
||||||
|
*.exe
|
||||||
|
*.exe.dlls/
|
||||||
|
*.exe.manifest
|
||||||
|
*.pc
|
||||||
31
LICENSE
Normal file
31
LICENSE
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
Copyright © 2024 Ryan. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. All advertising materials mentioning features or use of this software must
|
||||||
|
display the following acknowledgement:
|
||||||
|
|
||||||
|
This product includes software developed by Ryan, http://helloryan.se/.
|
||||||
|
|
||||||
|
4. Neither the name(s) of the copyright holder(s) nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from this
|
||||||
|
software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR
|
||||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||||
|
NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
14
README.md
Normal file
14
README.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# libart-seafire-routing
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
libart-seafire-routing is part of the Seafire HTTP/1.1 library for C++.
|
||||||
|
|
||||||
|
## Dedication
|
||||||
|
|
||||||
|
This project is dedicated to the memory of Sefyr.
|
||||||
|
|
||||||
|
## Sponsorship
|
||||||
|
|
||||||
|
You can sponsor the development of this project via Patreon. Read more
|
||||||
|
over at https://patreon.com/helloryan.
|
||||||
9
art/seafire/routing/.gitignore
vendored
Normal file
9
art/seafire/routing/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Generated version header.
|
||||||
|
#
|
||||||
|
version.hxx
|
||||||
|
|
||||||
|
# Unit test executables and Testscript output directories
|
||||||
|
# (can be symlinks).
|
||||||
|
#
|
||||||
|
*.test
|
||||||
|
test-*.test
|
||||||
43
art/seafire/routing/builder.cxx
Normal file
43
art/seafire/routing/builder.cxx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#include <art/seafire/routing/builder.hxx>
|
||||||
|
#include <art/seafire/routing/flatten.hxx>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace art::seafire::routing
|
||||||
|
{
|
||||||
|
|
||||||
|
builder_t::
|
||||||
|
builder_t()
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::list<virtual_host_t> const&
|
||||||
|
builder_t::
|
||||||
|
virtual_hosts() const
|
||||||
|
{
|
||||||
|
return _vhosts;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual_host_t&
|
||||||
|
builder_t::
|
||||||
|
add_virtual_host(std::string vhost)
|
||||||
|
{
|
||||||
|
_vhosts.emplace_back(std::move(vhost));
|
||||||
|
return _vhosts.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
routing_table_t
|
||||||
|
builder_t::
|
||||||
|
build() const
|
||||||
|
{
|
||||||
|
std::vector<endpoint_t> endpoints;
|
||||||
|
|
||||||
|
for (auto const& vhost : _vhosts) {
|
||||||
|
for (auto const& r : vhost.routes()) {
|
||||||
|
flatten(endpoints, vhost.host(), vhost.middleware(), r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return routing_table_t{std::move(endpoints)};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace art::seafire::routing
|
||||||
42
art/seafire/routing/builder.hxx
Normal file
42
art/seafire/routing/builder.hxx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#ifndef art__seafire__routing__builder_hxx_
|
||||||
|
#define art__seafire__routing__builder_hxx_
|
||||||
|
|
||||||
|
#include <art/seafire/routing/routing-table.hxx>
|
||||||
|
#include <art/seafire/routing/virtual-host.hxx>
|
||||||
|
|
||||||
|
#include <art/seafire/server/request-handler.hxx>
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace art::seafire::routing
|
||||||
|
{
|
||||||
|
|
||||||
|
class builder_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
builder_t();
|
||||||
|
|
||||||
|
builder_t(builder_t const&) = delete;
|
||||||
|
builder_t(builder_t&&) = delete;
|
||||||
|
|
||||||
|
std::list<virtual_host_t> const&
|
||||||
|
virtual_hosts() const;
|
||||||
|
|
||||||
|
virtual_host_t&
|
||||||
|
add_virtual_host(std::string);
|
||||||
|
|
||||||
|
routing_table_t
|
||||||
|
build() const;
|
||||||
|
|
||||||
|
builder_t& operator=(builder_t const&) = delete;
|
||||||
|
builder_t& operator=(builder_t&&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::list<virtual_host_t> _vhosts;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace art::seafire::routing
|
||||||
|
|
||||||
|
#endif
|
||||||
67
art/seafire/routing/buildfile
Normal file
67
art/seafire/routing/buildfile
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
intf_libs = # Interface dependencies.
|
||||||
|
impl_libs = # Implementation dependencies.
|
||||||
|
|
||||||
|
import impl_libs =+ libart-uri%lib{art-uri}
|
||||||
|
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}
|
||||||
|
|
||||||
|
./: lib{art-seafire-routing}: libul{art-seafire-routing}
|
||||||
|
|
||||||
|
libul{art-seafire-routing}: {hxx ixx txx cxx}{** -**.test... -version} \
|
||||||
|
{hxx }{ version}
|
||||||
|
|
||||||
|
libul{art-seafire-routing}: $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-routing}: 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-routing}:
|
||||||
|
{
|
||||||
|
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-routing}: bin.lib.version = "-$version.project_id"
|
||||||
|
else
|
||||||
|
lib{art-seafire-routing}: bin.lib.version = "-$version.major.$version.minor"
|
||||||
|
|
||||||
|
# Install into the art/seafire/routing/ subdirectory of, say, /usr/include/
|
||||||
|
# recreating subdirectories.
|
||||||
|
#
|
||||||
|
{hxx ixx txx}{*}:
|
||||||
|
{
|
||||||
|
install = include/art/seafire/routing/
|
||||||
|
install.subdirs = true
|
||||||
|
}
|
||||||
15
art/seafire/routing/diagnostics.cxx
Normal file
15
art/seafire/routing/diagnostics.cxx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#include <art/seafire/routing/diagnostics.hxx>
|
||||||
|
|
||||||
|
namespace art::seafire::routing
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Returns a reference to the routing diagnostic category.
|
||||||
|
///
|
||||||
|
common::diagnostics_t::category_t const&
|
||||||
|
routing_category()
|
||||||
|
{
|
||||||
|
static common::diagnostics_t::category_t category{"router"};
|
||||||
|
return category;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace art::seafire::routing
|
||||||
14
art/seafire/routing/diagnostics.hxx
Normal file
14
art/seafire/routing/diagnostics.hxx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef art__seafire__routing__diagnostics_hxx_
|
||||||
|
#define art__seafire__routing__diagnostics_hxx_
|
||||||
|
|
||||||
|
#include <art/seafire/common/diagnostics.hxx>
|
||||||
|
|
||||||
|
namespace art::seafire::routing
|
||||||
|
{
|
||||||
|
|
||||||
|
common::diagnostics_t::category_t const&
|
||||||
|
routing_category();
|
||||||
|
|
||||||
|
} // namespace art::seafire::routing
|
||||||
|
|
||||||
|
#endif
|
||||||
48
art/seafire/routing/endpoint.cxx
Normal file
48
art/seafire/routing/endpoint.cxx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#include <art/seafire/routing/endpoint.hxx>
|
||||||
|
|
||||||
|
namespace art::seafire::routing
|
||||||
|
{
|
||||||
|
|
||||||
|
endpoint_t::
|
||||||
|
endpoint_t(std::string host,
|
||||||
|
std::string path,
|
||||||
|
server::request_handler_t handler)
|
||||||
|
: host_{std::move(host)},
|
||||||
|
path_{std::move(path)},
|
||||||
|
handler_{std::move(handler)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string const&
|
||||||
|
endpoint_t::
|
||||||
|
host() const
|
||||||
|
{
|
||||||
|
return host_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string const&
|
||||||
|
endpoint_t::
|
||||||
|
path() const
|
||||||
|
{
|
||||||
|
return path_;
|
||||||
|
}
|
||||||
|
|
||||||
|
server::request_handler_t const&
|
||||||
|
endpoint_t::
|
||||||
|
handler() const
|
||||||
|
{
|
||||||
|
return handler_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream&
|
||||||
|
to_stream(std::ostream& o, endpoint_t const& ep)
|
||||||
|
{
|
||||||
|
return o << ep.host() << ": " << ep.path();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream&
|
||||||
|
operator<<(std::ostream& o, endpoint_t const& ep)
|
||||||
|
{
|
||||||
|
return to_stream(o, ep);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace art::seafire::routing
|
||||||
41
art/seafire/routing/endpoint.hxx
Normal file
41
art/seafire/routing/endpoint.hxx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#ifndef art__seafire__routing__endpoint_hxx_
|
||||||
|
#define art__seafire__routing__endpoint_hxx_
|
||||||
|
|
||||||
|
#include <art/seafire/server/request-handler.hxx>
|
||||||
|
|
||||||
|
#include <ostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace art::seafire::routing
|
||||||
|
{
|
||||||
|
|
||||||
|
class endpoint_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
endpoint_t(std::string, std::string, server::request_handler_t);
|
||||||
|
|
||||||
|
std::string const&
|
||||||
|
host() const;
|
||||||
|
|
||||||
|
std::string const&
|
||||||
|
path() const;
|
||||||
|
|
||||||
|
server::request_handler_t const&
|
||||||
|
handler() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string host_;
|
||||||
|
std::string path_;
|
||||||
|
server::request_handler_t handler_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream&
|
||||||
|
to_stream(std::ostream&, endpoint_t const&);
|
||||||
|
|
||||||
|
std::ostream&
|
||||||
|
operator<<(std::ostream&, endpoint_t const&);
|
||||||
|
|
||||||
|
} // namespace art::seafire::routing
|
||||||
|
|
||||||
|
#endif
|
||||||
81
art/seafire/routing/flatten.cxx
Normal file
81
art/seafire/routing/flatten.cxx
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#include <art/seafire/routing/flatten.hxx>
|
||||||
|
|
||||||
|
namespace art::seafire::routing
|
||||||
|
{
|
||||||
|
|
||||||
|
static
|
||||||
|
std::string
|
||||||
|
join_path(std::vector<std::string> const& segments)
|
||||||
|
{
|
||||||
|
std::string path{"/"};
|
||||||
|
|
||||||
|
bool first{true};
|
||||||
|
|
||||||
|
for (auto const& j : segments) {
|
||||||
|
if (j.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
path += "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
path += j;
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
flatten(std::vector<endpoint_t>& endpoints,
|
||||||
|
std::string const& vhost,
|
||||||
|
std::vector<server::middleware_t> middlewares,
|
||||||
|
route_t const& route,
|
||||||
|
std::vector<std::string> segments)
|
||||||
|
{
|
||||||
|
segments.emplace_back(route.path());
|
||||||
|
|
||||||
|
// append any middlewares if we have any.
|
||||||
|
//
|
||||||
|
for (auto const& m : route.middleware()) {
|
||||||
|
middlewares.emplace_back(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate an endpoint for this route if we have a handler.
|
||||||
|
//
|
||||||
|
if (auto const& h = route.handler()) {
|
||||||
|
endpoints.emplace_back(vhost, join_path(segments), server::make_middleware(middlewares, *h));
|
||||||
|
}
|
||||||
|
|
||||||
|
// flatten any child routes.
|
||||||
|
//
|
||||||
|
for (auto const& child_route : route.children()) {
|
||||||
|
flatten(endpoints,
|
||||||
|
vhost,
|
||||||
|
middlewares,
|
||||||
|
child_route,
|
||||||
|
segments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
flatten(std::vector<endpoint_t>& endpoints,
|
||||||
|
std::string const& vhost,
|
||||||
|
std::vector<server::middleware_t> middlewares,
|
||||||
|
route_t const& r)
|
||||||
|
{
|
||||||
|
flatten(endpoints, vhost, middlewares, r, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<endpoint_t>
|
||||||
|
flatten(std::string const& vhost, route_t const& r)
|
||||||
|
{
|
||||||
|
std::vector<endpoint_t> endpoints;
|
||||||
|
flatten(endpoints, vhost, {}, r);
|
||||||
|
return endpoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace art::seafire::routing
|
||||||
32
art/seafire/routing/flatten.hxx
Normal file
32
art/seafire/routing/flatten.hxx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#ifndef art__seafire__routing__flatten_route_hxx_
|
||||||
|
#define art__seafire__routing__flatten_route_hxx_
|
||||||
|
|
||||||
|
#include <art/seafire/routing/endpoint.hxx>
|
||||||
|
#include <art/seafire/routing/route.hxx>
|
||||||
|
|
||||||
|
#include <art/seafire/server/middleware.hxx>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace art::seafire::routing
|
||||||
|
{
|
||||||
|
|
||||||
|
void
|
||||||
|
flatten(std::vector<endpoint_t>& endpoints,
|
||||||
|
std::string const&,
|
||||||
|
std::vector<server::middleware_t>,
|
||||||
|
route_t const& r,
|
||||||
|
std::vector<std::string>);
|
||||||
|
|
||||||
|
void
|
||||||
|
flatten(std::vector<endpoint_t>&,
|
||||||
|
std::string const&,
|
||||||
|
std::vector<server::middleware_t>,
|
||||||
|
route_t const&);
|
||||||
|
|
||||||
|
std::vector<endpoint_t>
|
||||||
|
flatten(std::string const&, route_t const&);
|
||||||
|
|
||||||
|
} // namespace art::seafire::routing
|
||||||
|
|
||||||
|
#endif
|
||||||
63
art/seafire/routing/host-parameter.hxx
Normal file
63
art/seafire/routing/host-parameter.hxx
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#ifndef art__seafire__routing__host_parameter_hxx_
|
||||||
|
#define art__seafire__routing__host_parameter_hxx_
|
||||||
|
|
||||||
|
#include <art/seafire/server/parameters.hxx>
|
||||||
|
#include <art/seafire/server/request.hxx>
|
||||||
|
|
||||||
|
#include <art/seafire/routing/parameters.hxx>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace art::seafire::routing
|
||||||
|
{
|
||||||
|
|
||||||
|
template<
|
||||||
|
server::parameter_name_t Name,
|
||||||
|
typename ParameterType = server::string_parameter_t
|
||||||
|
>
|
||||||
|
class host_parameter_t
|
||||||
|
: public server::named_parameter_t<Name>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using parameter_type = ParameterType;
|
||||||
|
using value_type = typename parameter_type::value_type;
|
||||||
|
|
||||||
|
host_parameter_t(std::optional<value_type> value)
|
||||||
|
: _value{std::move(value)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
using server::named_parameter_t<Name>::name;
|
||||||
|
|
||||||
|
std::optional<value_type> const&
|
||||||
|
value() const
|
||||||
|
{
|
||||||
|
return _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator std::optional<value_type> const&() const
|
||||||
|
{
|
||||||
|
return value();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<value_type> const*
|
||||||
|
operator->() const
|
||||||
|
{
|
||||||
|
return &value();
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
host_parameter_t<Name, ParameterType>
|
||||||
|
fetch(server::request_t& req)
|
||||||
|
{
|
||||||
|
auto v = req.extensions().use<host_parameters_t>().get(name());
|
||||||
|
return parameter_type::try_parse(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::optional<value_type> _value;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace art::seafire::routing
|
||||||
|
|
||||||
|
#endif
|
||||||
115
art/seafire/routing/match.cxx
Normal file
115
art/seafire/routing/match.cxx
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
#include <art/seafire/routing/match.hxx>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace art::seafire::routing
|
||||||
|
{
|
||||||
|
|
||||||
|
bool
|
||||||
|
match(std::string const& subject,
|
||||||
|
std::string const& pattern,
|
||||||
|
char delim,
|
||||||
|
parameters_t& params)
|
||||||
|
{
|
||||||
|
parameters_t tmp_params;
|
||||||
|
|
||||||
|
// p/pend = pattern iterators.
|
||||||
|
//
|
||||||
|
auto p = pattern.begin();
|
||||||
|
auto const pend = pattern.end();
|
||||||
|
|
||||||
|
// s/send = subject iterators.
|
||||||
|
//
|
||||||
|
auto s = subject.begin();
|
||||||
|
auto const send = subject.end();
|
||||||
|
|
||||||
|
auto match_param = [&]()
|
||||||
|
{
|
||||||
|
// k/kend = local pattern iterators.
|
||||||
|
//
|
||||||
|
auto const k = p;
|
||||||
|
while (p != pend && '}' != *p) {
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
auto const kend = p;
|
||||||
|
++p;
|
||||||
|
|
||||||
|
if (k == kend) {
|
||||||
|
throw std::invalid_argument{"empty parameter name"};
|
||||||
|
}
|
||||||
|
|
||||||
|
// a greedy parameter will eat the rest of the subject.
|
||||||
|
//
|
||||||
|
bool const greedy{'*' == *k && 1 == std::distance(k, kend)};
|
||||||
|
|
||||||
|
// v/vend = local subject iterators.
|
||||||
|
//
|
||||||
|
auto const v = s;
|
||||||
|
while (s != send && (greedy || delim != *s)) {
|
||||||
|
++s;
|
||||||
|
}
|
||||||
|
auto const vend = s;
|
||||||
|
|
||||||
|
tmp_params.map().emplace(std::string{k, kend}, std::string{v, vend});
|
||||||
|
};
|
||||||
|
|
||||||
|
while (p != pend) {
|
||||||
|
if (*p == '{') {
|
||||||
|
++p;
|
||||||
|
match_param();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s == send) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p != *s) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
++p;
|
||||||
|
++s;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p != pend || s != send) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
params = std::move(tmp_params);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
render(std::string const& pattern, parameters_t& params)
|
||||||
|
{
|
||||||
|
std::stringstream str;
|
||||||
|
|
||||||
|
auto p = pattern.begin();
|
||||||
|
auto pend = pattern.end();
|
||||||
|
|
||||||
|
while (p != pend) {
|
||||||
|
if (*p == '{') {
|
||||||
|
++p;
|
||||||
|
|
||||||
|
auto const k = p;
|
||||||
|
while (p != pend && '}' != *p) {
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
auto const kend = p;
|
||||||
|
|
||||||
|
str << params.get(std::string{k, kend}).value_or("<unknown>");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
str << *p;
|
||||||
|
}
|
||||||
|
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace art::seafire::routing
|
||||||
22
art/seafire/routing/match.hxx
Normal file
22
art/seafire/routing/match.hxx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#ifndef art__seafire__routing__match_hxx_
|
||||||
|
#define art__seafire__routing__match_hxx_
|
||||||
|
|
||||||
|
#include <art/seafire/routing/parameters.hxx>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace art::seafire::routing
|
||||||
|
{
|
||||||
|
|
||||||
|
bool
|
||||||
|
match(std::string const&,
|
||||||
|
std::string const&,
|
||||||
|
char,
|
||||||
|
parameters_t&);
|
||||||
|
|
||||||
|
std::string
|
||||||
|
render(std::string const&, parameters_t&);
|
||||||
|
|
||||||
|
} // namespace art::seafire::routing
|
||||||
|
|
||||||
|
#endif
|
||||||
30
art/seafire/routing/parameters.cxx
Normal file
30
art/seafire/routing/parameters.cxx
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#include <art/seafire/routing/parameters.hxx>
|
||||||
|
|
||||||
|
namespace art::seafire::routing
|
||||||
|
{
|
||||||
|
|
||||||
|
parameters_t::map_type&
|
||||||
|
parameters_t::
|
||||||
|
map()
|
||||||
|
{
|
||||||
|
return _values;
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters_t::map_type const&
|
||||||
|
parameters_t::
|
||||||
|
map() const
|
||||||
|
{
|
||||||
|
return _values;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string>
|
||||||
|
parameters_t::
|
||||||
|
get(std::string const& key) const
|
||||||
|
{
|
||||||
|
if (auto it = map().find(key); it != map().end())
|
||||||
|
return it->second;
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace art::seafire::routing
|
||||||
56
art/seafire/routing/parameters.hxx
Normal file
56
art/seafire/routing/parameters.hxx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#ifndef seafire_routing__parameters_hxx_
|
||||||
|
#define seafire_routing__parameters_hxx_
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace art::seafire::routing
|
||||||
|
{
|
||||||
|
|
||||||
|
class parameters_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using map_type = std::map<std::string, std::string>;
|
||||||
|
|
||||||
|
parameters_t() = default;
|
||||||
|
|
||||||
|
parameters_t(map_type values)
|
||||||
|
: _values{std::move(values)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
map_type&
|
||||||
|
map();
|
||||||
|
|
||||||
|
map_type const&
|
||||||
|
map() const;
|
||||||
|
|
||||||
|
std::optional<std::string>
|
||||||
|
get(std::string const&) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
map_type _values;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class host_parameters_t
|
||||||
|
: public parameters_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using parameters_t::parameters_t;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class route_parameters_t
|
||||||
|
: public parameters_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using parameters_t::parameters_t;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace art::seafire::routing
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
63
art/seafire/routing/route-parameter.hxx
Normal file
63
art/seafire/routing/route-parameter.hxx
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#ifndef art__seafire__routing__route_parameter_hxx_
|
||||||
|
#define art__seafire__routing__route_parameter_hxx_
|
||||||
|
|
||||||
|
#include <art/seafire/server/parameters.hxx>
|
||||||
|
#include <art/seafire/server/request.hxx>
|
||||||
|
|
||||||
|
#include <art/seafire/routing/parameters.hxx>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace art::seafire::routing
|
||||||
|
{
|
||||||
|
|
||||||
|
template<
|
||||||
|
server::parameter_name_t Name,
|
||||||
|
typename ParameterType = server::string_parameter_t
|
||||||
|
>
|
||||||
|
class route_parameter_t
|
||||||
|
: public server::named_parameter_t<Name>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using parameter_type = ParameterType;
|
||||||
|
using value_type = typename parameter_type::value_type;
|
||||||
|
|
||||||
|
route_parameter_t(std::optional<value_type> value)
|
||||||
|
: _value{std::move(value)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
using server::named_parameter_t<Name>::name;
|
||||||
|
|
||||||
|
std::optional<value_type> const&
|
||||||
|
value() const
|
||||||
|
{
|
||||||
|
return _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator std::optional<value_type> const&() const
|
||||||
|
{
|
||||||
|
return value();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<value_type> const*
|
||||||
|
operator->() const
|
||||||
|
{
|
||||||
|
return &value();
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
route_parameter_t<Name, ParameterType>
|
||||||
|
fetch(server::request_t& req)
|
||||||
|
{
|
||||||
|
auto v = req.extensions().use<route_parameters_t>().get(name());
|
||||||
|
return parameter_type::try_parse(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::optional<value_type> _value;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace art::seafire::routing
|
||||||
|
|
||||||
|
#endif
|
||||||
131
art/seafire/routing/route.cxx
Normal file
131
art/seafire/routing/route.cxx
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
#include <art/seafire/routing/route.hxx>
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace art::seafire::routing
|
||||||
|
{
|
||||||
|
|
||||||
|
static
|
||||||
|
std::string
|
||||||
|
validate_path(std::string path)
|
||||||
|
{
|
||||||
|
if (!path.empty()) {
|
||||||
|
if (path.front() == '/') {
|
||||||
|
throw std::invalid_argument{"route path must not begin with '/'"};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.back() == '/') {
|
||||||
|
throw std::invalid_argument{"route path must not end with '/'"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
route_t::
|
||||||
|
route_t() = default;
|
||||||
|
|
||||||
|
route_t::
|
||||||
|
route_t(std::string path)
|
||||||
|
: path_{validate_path(std::move(path))}
|
||||||
|
{}
|
||||||
|
|
||||||
|
route_t::
|
||||||
|
route_t(std::string path, server::request_handler_t handler)
|
||||||
|
: path_{validate_path(std::move(path))}, handler_{std::move(handler)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string const&
|
||||||
|
route_t::
|
||||||
|
path() const
|
||||||
|
{
|
||||||
|
return path_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<server::middleware_t> const&
|
||||||
|
route_t::
|
||||||
|
middleware() const
|
||||||
|
{
|
||||||
|
return middleware_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<server::request_handler_t> const&
|
||||||
|
route_t::
|
||||||
|
handler() const
|
||||||
|
{
|
||||||
|
return handler_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<route_t> const&
|
||||||
|
route_t::
|
||||||
|
children() const
|
||||||
|
{
|
||||||
|
return children_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
route_t::
|
||||||
|
use(server::middleware_t m)
|
||||||
|
{
|
||||||
|
middleware_.emplace_back(std::move(m));
|
||||||
|
}
|
||||||
|
|
||||||
|
route_t&
|
||||||
|
route_t::
|
||||||
|
add_route()
|
||||||
|
{
|
||||||
|
children_.emplace_back();
|
||||||
|
return children_.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
route_t&
|
||||||
|
route_t::
|
||||||
|
add_route(std::string path)
|
||||||
|
{
|
||||||
|
children_.emplace_back(std::move(path));
|
||||||
|
return children_.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
route_t&
|
||||||
|
route_t::
|
||||||
|
add_route(std::string path, server::request_handler_t handler)
|
||||||
|
{
|
||||||
|
children_.emplace_back(std::move(path), std::move(handler));
|
||||||
|
return children_.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream&
|
||||||
|
to_stream(std::ostream& o, route_t const& r, std::size_t indent)
|
||||||
|
{
|
||||||
|
if (indent > 0) {
|
||||||
|
o << std::string(indent, ' ');
|
||||||
|
o << "-> ";
|
||||||
|
}
|
||||||
|
|
||||||
|
o << "route: '" << r.path() << '\'';
|
||||||
|
|
||||||
|
if (!r.middleware().empty()) {
|
||||||
|
o << " (with middleware)";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!r.handler()) {
|
||||||
|
o << " (null handler)";
|
||||||
|
}
|
||||||
|
|
||||||
|
o << '\n';
|
||||||
|
|
||||||
|
for (auto const& child : r.children()) {
|
||||||
|
to_stream(o, child, indent + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream&
|
||||||
|
operator<<(std::ostream& o, route_t const& route)
|
||||||
|
{
|
||||||
|
return to_stream(o, route, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace art::seafire::routing
|
||||||
77
art/seafire/routing/route.hxx
Normal file
77
art/seafire/routing/route.hxx
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#ifndef art__seafire__routing__route_hxx_
|
||||||
|
#define art__seafire__routing__route_hxx_
|
||||||
|
|
||||||
|
#include <art/seafire/routing/endpoint.hxx>
|
||||||
|
|
||||||
|
#include <art/seafire/server/middleware.hxx>
|
||||||
|
#include <art/seafire/server/request-handler.hxx>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <list>
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace art::seafire::routing
|
||||||
|
{
|
||||||
|
|
||||||
|
class route_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
route_t();
|
||||||
|
|
||||||
|
explicit
|
||||||
|
route_t(std::string);
|
||||||
|
|
||||||
|
route_t(std::string, server::request_handler_t);
|
||||||
|
|
||||||
|
route_t(route_t const&) = delete;
|
||||||
|
route_t(route_t&&) = delete;
|
||||||
|
|
||||||
|
std::string const&
|
||||||
|
path() const;
|
||||||
|
|
||||||
|
std::vector<server::middleware_t> const&
|
||||||
|
middleware() const;
|
||||||
|
|
||||||
|
std::optional<server::request_handler_t> const&
|
||||||
|
handler() const;
|
||||||
|
|
||||||
|
std::list<route_t> const&
|
||||||
|
children() const;
|
||||||
|
|
||||||
|
void
|
||||||
|
use(server::middleware_t);
|
||||||
|
|
||||||
|
route_t&
|
||||||
|
add_route();
|
||||||
|
|
||||||
|
route_t&
|
||||||
|
add_route(std::string);
|
||||||
|
|
||||||
|
route_t&
|
||||||
|
add_route(std::string, server::request_handler_t);
|
||||||
|
|
||||||
|
route_t& operator=(route_t const&) = delete;
|
||||||
|
route_t& operator=(route_t&&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string path_;
|
||||||
|
std::vector<server::middleware_t> middleware_;
|
||||||
|
std::optional<server::request_handler_t> handler_;
|
||||||
|
|
||||||
|
// must be std::list to prevent invalidation of references to
|
||||||
|
// individual routes.
|
||||||
|
//
|
||||||
|
std::list<route_t> children_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream&
|
||||||
|
to_stream(std::ostream&, route_t const&, std::size_t);
|
||||||
|
|
||||||
|
std::ostream&
|
||||||
|
operator<<(std::ostream&, route_t const&);
|
||||||
|
|
||||||
|
} // namespace art::seafire::routing
|
||||||
|
|
||||||
|
#endif
|
||||||
122
art/seafire/routing/router.cxx
Normal file
122
art/seafire/routing/router.cxx
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
#include <art/seafire/routing/router.hxx>
|
||||||
|
#include <art/seafire/routing/diagnostics.hxx>
|
||||||
|
|
||||||
|
#include <art/seafire/protocol/rfc7230/host.hxx>
|
||||||
|
|
||||||
|
namespace art::seafire::routing
|
||||||
|
{
|
||||||
|
|
||||||
|
static
|
||||||
|
std::string
|
||||||
|
normalize_path(std::string const& path)
|
||||||
|
{
|
||||||
|
std::stringstream ipath{path};
|
||||||
|
std::vector<std::string> segments;
|
||||||
|
|
||||||
|
for (std::string segment; std::getline(ipath, segment, '/');) {
|
||||||
|
if (segment.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (segment == ".") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (segment == "..") {
|
||||||
|
if (!segments.empty()) {
|
||||||
|
segments.pop_back();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
segments.push_back(segment);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string normalized;
|
||||||
|
|
||||||
|
for (auto const& j : segments) {
|
||||||
|
normalized += '/';
|
||||||
|
normalized += j;
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalized.empty() ? "/" : normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
router_t::
|
||||||
|
router_t(common::diagnostics_t& diagnostics, routing_table_t table)
|
||||||
|
: diagnostics_{diagnostics}, rt_{std::move(table)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
routing_table_t const&
|
||||||
|
router_t::
|
||||||
|
routing_table() const
|
||||||
|
{
|
||||||
|
return rt_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
router_t::
|
||||||
|
on_request(server::request_t& req, server::response_t& res) const
|
||||||
|
{
|
||||||
|
trace() << "on_request(...)";
|
||||||
|
|
||||||
|
auto host = get<art::seafire::protocol::rfc7230::host_t>(req);
|
||||||
|
|
||||||
|
if (!host) {
|
||||||
|
trace() << "host not present on request!";
|
||||||
|
res.send(server::common_error_t::not_found);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto hostname = host->hostname();
|
||||||
|
auto path = normalize_path(req.get_message().target_uri().path_str());
|
||||||
|
|
||||||
|
trace() << "locating endpoint:\n"
|
||||||
|
<< " -> hostname: " << hostname << '\n'
|
||||||
|
<< " -> path : " << path
|
||||||
|
;
|
||||||
|
|
||||||
|
auto result = routing_table().find_route(hostname, path);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
trace() << "endpoint for [" << path << "] not found";
|
||||||
|
res.send(server::common_error_t::not_found);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto trace_endpoint = [&result](common::diagnostics_t::proxy_t proxy)
|
||||||
|
{
|
||||||
|
proxy << "endpoint found!";
|
||||||
|
|
||||||
|
for (auto const& j : result->host_params.map())
|
||||||
|
proxy << "\n -> host param : " << j.first << " = " << j.second;
|
||||||
|
|
||||||
|
for (auto const& j : result->route_params.map())
|
||||||
|
proxy << "\n -> route param: " << j.first << " = " << j.second;
|
||||||
|
|
||||||
|
proxy << '\n';
|
||||||
|
};
|
||||||
|
|
||||||
|
trace_endpoint(trace());
|
||||||
|
|
||||||
|
req.extensions().extend(&res.allocator().alloc(result->host_params));
|
||||||
|
req.extensions().extend(&res.allocator().alloc(result->route_params));
|
||||||
|
|
||||||
|
result->handler.invoke(req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
router_t::
|
||||||
|
operator()(server::request_t& req, server::response_t& res) const
|
||||||
|
{
|
||||||
|
on_request(req, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
common::diagnostics_t::proxy_t
|
||||||
|
router_t::
|
||||||
|
trace() const
|
||||||
|
{
|
||||||
|
return diagnostics_ << routing_category();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace art::seafire::routing
|
||||||
40
art/seafire/routing/router.hxx
Normal file
40
art/seafire/routing/router.hxx
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#ifndef art__seafire__routing__router_hxx_
|
||||||
|
#define art__seafire__routing__router_hxx_
|
||||||
|
|
||||||
|
#include <art/seafire/routing/diagnostics.hxx>
|
||||||
|
#include <art/seafire/routing/routing-table.hxx>
|
||||||
|
|
||||||
|
#include <art/seafire/common/diagnostics.hxx>
|
||||||
|
|
||||||
|
#include <art/seafire/server/request.hxx>
|
||||||
|
#include <art/seafire/server/response.hxx>
|
||||||
|
|
||||||
|
namespace art::seafire::routing
|
||||||
|
{
|
||||||
|
|
||||||
|
class router_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
router_t(common::diagnostics_t&, routing_table_t);
|
||||||
|
|
||||||
|
routing_table_t const&
|
||||||
|
routing_table() const;
|
||||||
|
|
||||||
|
void
|
||||||
|
on_request(server::request_t&, server::response_t&) const;
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(server::request_t&, server::response_t&) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
common::diagnostics_t::proxy_t
|
||||||
|
trace() const;
|
||||||
|
|
||||||
|
common::diagnostics_t& diagnostics_;
|
||||||
|
routing_table_t rt_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace art::seafire::routing
|
||||||
|
|
||||||
|
#endif
|
||||||
45
art/seafire/routing/routing-table.cxx
Normal file
45
art/seafire/routing/routing-table.cxx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#include <art/seafire/routing/routing-table.hxx>
|
||||||
|
|
||||||
|
#include <art/seafire/routing/match.hxx>
|
||||||
|
|
||||||
|
namespace art::seafire::routing
|
||||||
|
{
|
||||||
|
|
||||||
|
routing_table_t::
|
||||||
|
routing_table_t(std::vector<endpoint_t> endpoints)
|
||||||
|
: endpoints_{std::move(endpoints)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::vector<endpoint_t> const&
|
||||||
|
routing_table_t::
|
||||||
|
endpoints() const
|
||||||
|
{
|
||||||
|
return endpoints_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<routing_table_t::find_result_t>
|
||||||
|
routing_table_t::
|
||||||
|
find_route(std::string const& host, std::string const& path) const
|
||||||
|
{
|
||||||
|
for (auto const& e : endpoints()) {
|
||||||
|
host_parameters_t host_params;
|
||||||
|
|
||||||
|
if (!match(host, e.host(), '.', host_params)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
route_parameters_t route_params;
|
||||||
|
|
||||||
|
if (match(path, e.path(), '/', route_params)) {
|
||||||
|
return find_result_t{
|
||||||
|
std::move(host_params),
|
||||||
|
std::move(route_params),
|
||||||
|
e.handler()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace art::seafire::routing
|
||||||
50
art/seafire/routing/routing-table.hxx
Normal file
50
art/seafire/routing/routing-table.hxx
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#ifndef art__seafire__routing__routing_table_hxx_
|
||||||
|
#define art__seafire__routing__routing_table_hxx_
|
||||||
|
|
||||||
|
#include <art/seafire/routing/endpoint.hxx>
|
||||||
|
#include <art/seafire/routing/parameters.hxx>
|
||||||
|
#include <art/seafire/routing/route.hxx>
|
||||||
|
|
||||||
|
#include <art/seafire/server/request-handler.hxx>
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace art::seafire::routing
|
||||||
|
{
|
||||||
|
|
||||||
|
class routing_table_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct find_result_t
|
||||||
|
{
|
||||||
|
host_parameters_t host_params;
|
||||||
|
route_parameters_t route_params;
|
||||||
|
|
||||||
|
server::request_handler_t const& handler;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit
|
||||||
|
routing_table_t(std::vector<endpoint_t>);
|
||||||
|
|
||||||
|
std::vector<endpoint_t> const&
|
||||||
|
endpoints() const;
|
||||||
|
|
||||||
|
std::optional<find_result_t>
|
||||||
|
find_route(std::string const&, std::string const&) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static
|
||||||
|
bool
|
||||||
|
match_host(std::string const&, std::string const&);
|
||||||
|
|
||||||
|
std::vector<endpoint_t> endpoints_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace art::seafire::routing
|
||||||
|
|
||||||
|
#endif
|
||||||
37
art/seafire/routing/version.hxx.in
Normal file
37
art/seafire/routing/version.hxx.in
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#ifndef art__seafire__routing__version_hxx_
|
||||||
|
#define art__seafire__routing__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_ROUTING_VERSION $libart_seafire_routing.version.project_number$ULL
|
||||||
|
#define LIBART_SEAFIRE_ROUTING_VERSION_STR "$libart_seafire_routing.version.project$"
|
||||||
|
#define LIBART_SEAFIRE_ROUTING_VERSION_ID "$libart_seafire_routing.version.project_id$"
|
||||||
|
#define LIBART_SEAFIRE_ROUTING_VERSION_FULL "$libart_seafire_routing.version$"
|
||||||
|
|
||||||
|
#define LIBART_SEAFIRE_ROUTING_VERSION_MAJOR $libart_seafire_routing.version.major$
|
||||||
|
#define LIBART_SEAFIRE_ROUTING_VERSION_MINOR $libart_seafire_routing.version.minor$
|
||||||
|
#define LIBART_SEAFIRE_ROUTING_VERSION_PATCH $libart_seafire_routing.version.patch$
|
||||||
|
|
||||||
|
#define LIBART_SEAFIRE_ROUTING_PRE_RELEASE $libart_seafire_routing.version.pre_release$
|
||||||
|
|
||||||
|
#define LIBART_SEAFIRE_ROUTING_SNAPSHOT_SN $libart_seafire_routing.version.snapshot_sn$ULL
|
||||||
|
#define LIBART_SEAFIRE_ROUTING_SNAPSHOT_ID "$libart_seafire_routing.version.snapshot_id$"
|
||||||
|
|
||||||
|
#endif
|
||||||
100
art/seafire/routing/virtual-host.cxx
Normal file
100
art/seafire/routing/virtual-host.cxx
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
#include <art/seafire/routing/virtual-host.hxx>
|
||||||
|
|
||||||
|
namespace art::seafire::routing
|
||||||
|
{
|
||||||
|
|
||||||
|
static
|
||||||
|
std::string
|
||||||
|
validate_host(std::string host)
|
||||||
|
{
|
||||||
|
if (host.empty()) {
|
||||||
|
throw std::invalid_argument{"host must not be empty"};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host.front() == '.') {
|
||||||
|
throw std::invalid_argument{"host must not begin with '.'"};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host.back() == '.') {
|
||||||
|
throw std::invalid_argument{"host must not end with '.'"};
|
||||||
|
}
|
||||||
|
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual_host_t::
|
||||||
|
virtual_host_t(std::string host)
|
||||||
|
: _host{validate_host(std::move(host))}
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string const&
|
||||||
|
virtual_host_t::
|
||||||
|
host() const
|
||||||
|
{
|
||||||
|
return _host;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<server::middleware_t> const&
|
||||||
|
virtual_host_t::
|
||||||
|
middleware() const
|
||||||
|
{
|
||||||
|
return _middleware;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<route_t> const&
|
||||||
|
virtual_host_t::
|
||||||
|
routes() const
|
||||||
|
{
|
||||||
|
return _routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
virtual_host_t::
|
||||||
|
use(server::middleware_t m)
|
||||||
|
{
|
||||||
|
_middleware.emplace_back(std::move(m));
|
||||||
|
}
|
||||||
|
|
||||||
|
route_t&
|
||||||
|
virtual_host_t::
|
||||||
|
add_route()
|
||||||
|
{
|
||||||
|
_routes.emplace_back();
|
||||||
|
return _routes.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
route_t&
|
||||||
|
virtual_host_t::
|
||||||
|
add_route(std::string path)
|
||||||
|
{
|
||||||
|
_routes.emplace_back(std::move(path));
|
||||||
|
return _routes.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
route_t&
|
||||||
|
virtual_host_t::
|
||||||
|
add_route(std::string path, server::request_handler_t handler)
|
||||||
|
{
|
||||||
|
_routes.emplace_back(std::move(path), std::move(handler));
|
||||||
|
return _routes.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream&
|
||||||
|
to_stream(std::ostream& o, virtual_host_t const& vhost)
|
||||||
|
{
|
||||||
|
o << "virtual host: '" << vhost.host() << "'\n";
|
||||||
|
|
||||||
|
for (auto const& r : vhost.routes()) {
|
||||||
|
to_stream(o, r, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream&
|
||||||
|
operator<<(std::ostream& o, virtual_host_t const& vhost)
|
||||||
|
{
|
||||||
|
return to_stream(o, vhost);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace art::seafire::routing
|
||||||
65
art/seafire/routing/virtual-host.hxx
Normal file
65
art/seafire/routing/virtual-host.hxx
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#ifndef seafire_routing__virtual_host_hxx_
|
||||||
|
#define seafire_routing__virtual_host_hxx_
|
||||||
|
|
||||||
|
#include <art/seafire/routing/route.hxx>
|
||||||
|
|
||||||
|
#include <art/seafire/server/middleware.hxx>
|
||||||
|
#include <art/seafire/server/request-handler.hxx>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <list>
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace art::seafire::routing
|
||||||
|
{
|
||||||
|
|
||||||
|
class virtual_host_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit
|
||||||
|
virtual_host_t(std::string);
|
||||||
|
|
||||||
|
virtual_host_t(virtual_host_t const&) = delete;
|
||||||
|
virtual_host_t(virtual_host_t&&) = delete;
|
||||||
|
|
||||||
|
std::string const&
|
||||||
|
host() const;
|
||||||
|
|
||||||
|
std::vector<server::middleware_t> const&
|
||||||
|
middleware() const;
|
||||||
|
|
||||||
|
std::list<route_t> const&
|
||||||
|
routes() const;
|
||||||
|
|
||||||
|
void
|
||||||
|
use(server::middleware_t);
|
||||||
|
|
||||||
|
route_t&
|
||||||
|
add_route();
|
||||||
|
|
||||||
|
route_t&
|
||||||
|
add_route(std::string);
|
||||||
|
|
||||||
|
route_t&
|
||||||
|
add_route(std::string, server::request_handler_t);
|
||||||
|
|
||||||
|
virtual_host_t& operator=(virtual_host_t const&) = delete;
|
||||||
|
virtual_host_t& operator=(virtual_host_t&&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string _host;
|
||||||
|
std::vector<server::middleware_t> _middleware;
|
||||||
|
std::list<route_t> _routes;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream&
|
||||||
|
to_stream(std::ostream&, virtual_host_t const&);
|
||||||
|
|
||||||
|
std::ostream&
|
||||||
|
operator<<(std::ostream&, virtual_host_t const&);
|
||||||
|
|
||||||
|
} // namespace art::seafire::routing
|
||||||
|
|
||||||
|
#endif
|
||||||
4
build/.gitignore
vendored
Normal file
4
build/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
/config.build
|
||||||
|
/root/
|
||||||
|
/bootstrap/
|
||||||
|
build/
|
||||||
7
build/bootstrap.build
Normal file
7
build/bootstrap.build
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
project = libart-seafire-routing
|
||||||
|
|
||||||
|
using version
|
||||||
|
using config
|
||||||
|
using test
|
||||||
|
using install
|
||||||
|
using dist
|
||||||
6
build/export.build
Normal file
6
build/export.build
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
$out_root/
|
||||||
|
{
|
||||||
|
include art/seafire/routing/
|
||||||
|
}
|
||||||
|
|
||||||
|
export $out_root/art/seafire/routing/$import.target
|
||||||
16
build/root.build
Normal file
16
build/root.build
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Uncomment to suppress warnings coming from external libraries.
|
||||||
|
#
|
||||||
|
#cxx.internal.scope = current
|
||||||
|
|
||||||
|
cxx.std = latest
|
||||||
|
|
||||||
|
using cxx
|
||||||
|
|
||||||
|
hxx{*}: extension = hxx
|
||||||
|
ixx{*}: extension = ixx
|
||||||
|
txx{*}: extension = txx
|
||||||
|
cxx{*}: extension = cxx
|
||||||
|
|
||||||
|
# The test target for cross-testing (running tests under Wine, etc).
|
||||||
|
#
|
||||||
|
test.target = $cxx.target
|
||||||
5
buildfile
Normal file
5
buildfile
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
./: {art/ tests/} doc{README.md} legal{LICENSE} manifest
|
||||||
|
|
||||||
|
# Don't install tests.
|
||||||
|
#
|
||||||
|
tests/: install = false
|
||||||
15
manifest
Normal file
15
manifest
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
: 1
|
||||||
|
name: libart-seafire-routing
|
||||||
|
version: 0.1.0-a.0.z
|
||||||
|
language: c++
|
||||||
|
summary: Seafire Routing C++ library
|
||||||
|
license: BSD-4-Clause
|
||||||
|
description-file: README.md
|
||||||
|
url: https://art.helloryan.se/
|
||||||
|
email: art@helloryan.se
|
||||||
|
depends: * build2 >= 0.17.0
|
||||||
|
depends: * bpkg >= 0.17.0
|
||||||
|
depends: libart-uri ^0.1.0-
|
||||||
|
depends: libart-seafire-common ^0.1.0-
|
||||||
|
depends: libart-seafire-protocol ^0.1.0-
|
||||||
|
depends: libart-seafire-server ^0.1.0-
|
||||||
23
repositories.manifest
Normal file
23
repositories.manifest
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
: 1
|
||||||
|
summary: Seafire-Routing project repository
|
||||||
|
|
||||||
|
:
|
||||||
|
role: prerequisite
|
||||||
|
location: https://pkg.cppget.org/1/beta
|
||||||
|
trust: 70:64:FE:E4:E0:F3:60:F1:B4:51:E1:FA:12:5C:E0:B3:DB:DF:96:33:39:B9:2E:E5:C2:68:63:4C:A6:47:39:43
|
||||||
|
|
||||||
|
:
|
||||||
|
role: prerequisite
|
||||||
|
location: https://code.helloryan.se/art/libart-uri.git##HEAD
|
||||||
|
|
||||||
|
:
|
||||||
|
role: prerequisite
|
||||||
|
location: https://code.helloryan.se/art/libart-seafire-common.git##HEAD
|
||||||
|
|
||||||
|
:
|
||||||
|
role: prerequisite
|
||||||
|
location: https://code.helloryan.se/art/libart-seafire-protocol.git##HEAD
|
||||||
|
|
||||||
|
:
|
||||||
|
role: prerequisite
|
||||||
|
location: https://code.helloryan.se/art/libart-seafire-server.git##HEAD
|
||||||
8
tests/.gitignore
vendored
Normal file
8
tests/.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Test executables.
|
||||||
|
#
|
||||||
|
driver
|
||||||
|
|
||||||
|
# Testscript output directories (can be symlinks).
|
||||||
|
#
|
||||||
|
test
|
||||||
|
test-*
|
||||||
4
tests/build/.gitignore
vendored
Normal file
4
tests/build/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
/config.build
|
||||||
|
/root/
|
||||||
|
/bootstrap/
|
||||||
|
build/
|
||||||
5
tests/build/bootstrap.build
Normal file
5
tests/build/bootstrap.build
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
project = # Unnamed tests subproject.
|
||||||
|
|
||||||
|
using config
|
||||||
|
using test
|
||||||
|
using dist
|
||||||
16
tests/build/root.build
Normal file
16
tests/build/root.build
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
cxx.std = latest
|
||||||
|
|
||||||
|
using cxx
|
||||||
|
|
||||||
|
hxx{*}: extension = hxx
|
||||||
|
ixx{*}: extension = ixx
|
||||||
|
txx{*}: extension = txx
|
||||||
|
cxx{*}: extension = cxx
|
||||||
|
|
||||||
|
# Every exe{} in this subproject is by default a test.
|
||||||
|
#
|
||||||
|
exe{*}: test = true
|
||||||
|
|
||||||
|
# The test target for cross-testing (running tests under Wine, etc).
|
||||||
|
#
|
||||||
|
test.target = $cxx.target
|
||||||
1
tests/buildfile
Normal file
1
tests/buildfile
Normal file
@@ -0,0 +1 @@
|
|||||||
|
./: {*/ -build/}
|
||||||
Reference in New Issue
Block a user