Hello libcode-seafire-server
This commit is contained in:
commit
ef1e544ff1
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
|
24
.gitea/workflows/on-push.yaml
Normal file
24
.gitea/workflows/on-push.yaml
Normal file
@ -0,0 +1,24 @@
|
||||
name: on-push
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
runs-on: linux
|
||||
container: code.helloryan.se/infra/buildenv/cxx-amd64-fedora-40:latest
|
||||
volumes:
|
||||
- /build
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Authenticate
|
||||
run: |
|
||||
git config unset http.https://code.helloryan.se/.extraheader
|
||||
echo "${{ secrets.NETRC }}" >> ~/.netrc
|
||||
- name: Initialize
|
||||
run: |
|
||||
bpkg create -d /build cc config.cc.coptions="-Wall -Werror"
|
||||
bdep init -A /build
|
||||
- name: Build
|
||||
run: b
|
||||
- name: Test
|
||||
run: b test
|
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.
|
21
README.md
Normal file
21
README.md
Normal file
@ -0,0 +1,21 @@
|
||||
# libcode-seafire-server
|
||||
|
||||
![Build status](https://code.helloryan.se/code/libcode-seafire-server/actions/workflows/on-push.yaml/badge.svg)
|
||||
|
||||
## Requirements
|
||||
|
||||
None, other than a modern C++-compiler.
|
||||
|
||||
## Building
|
||||
|
||||
See the wiki, https://code.helloryan.se/code/wiki/wiki/Build-Instructions, for
|
||||
build instructions.
|
||||
|
||||
## Contact
|
||||
|
||||
Please report bugs and issues by sending an e-mail to: ryan@helloryan.se.
|
||||
|
||||
## Contributing
|
||||
|
||||
Please send an e-mail to ryan@helloryan.se to request an account and
|
||||
write-access to the libcode-seafire-server repository.
|
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 = libcode-seafire-server
|
||||
|
||||
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 code/seafire/server/
|
||||
}
|
||||
|
||||
export $out_root/code/seafire/server/$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 @@
|
||||
./: {code/ tests/} doc{README.md} legal{LICENSE} manifest
|
||||
|
||||
# Don't install tests.
|
||||
#
|
||||
tests/: install = false
|
9
code/seafire/server/.gitignore
vendored
Normal file
9
code/seafire/server/.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
|
68
code/seafire/server/buildfile
Normal file
68
code/seafire/server/buildfile
Normal file
@ -0,0 +1,68 @@
|
||||
intf_libs = # Interface dependencies.
|
||||
impl_libs = # Implementation dependencies.
|
||||
|
||||
import intf_libs =+ libasio%lib{asio}
|
||||
import intf_libs =+ libcode-uri%lib{code-uri}
|
||||
import intf_libs =+ libcode-seafire-common%lib{code-seafire-common}
|
||||
import intf_libs =+ libcode-seafire-protocol%lib{code-seafire-protocol}
|
||||
|
||||
./: lib{code-seafire-server}: libul{code-seafire-server}
|
||||
|
||||
libul{code-seafire-server}: {hxx ixx txx cxx}{** -**.test... -version} \
|
||||
{hxx }{ version}
|
||||
|
||||
libul{code-seafire-server}: $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{code-seafire-server}: 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"
|
||||
cxx.coptions =+ -fvisibility=hidden
|
||||
|
||||
# Export options.
|
||||
#
|
||||
lib{code-seafire-server}:
|
||||
{
|
||||
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{code-seafire-server}: bin.lib.version = "-$version.project_id"
|
||||
else
|
||||
lib{code-seafire-server}: bin.lib.version = "-$version.major.$version.minor"
|
||||
|
||||
# Install into the code/seafire/server/ subdirectory of, say, /usr/include/
|
||||
# recreating subdirectories.
|
||||
#
|
||||
{hxx ixx txx}{*}:
|
||||
{
|
||||
install = include/code/seafire/server/
|
||||
install.subdirs = true
|
||||
}
|
52
code/seafire/server/common-error.hxx
Normal file
52
code/seafire/server/common-error.hxx
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef code__seafire__server__common_error_hxx_
|
||||
#define code__seafire__server__common_error_hxx_
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
enum class common_error_t
|
||||
{
|
||||
// RCF 7231.
|
||||
//
|
||||
bad_request,
|
||||
payment_required,
|
||||
forbidden,
|
||||
not_found,
|
||||
method_not_allowed,
|
||||
not_acceptable,
|
||||
request_timeout,
|
||||
conflict,
|
||||
gone,
|
||||
length_required,
|
||||
payload_too_large,
|
||||
uri_too_long,
|
||||
unsupported_media_type,
|
||||
expectation_failed,
|
||||
upgrade_required,
|
||||
|
||||
internal_server_error,
|
||||
not_implemented,
|
||||
bad_gateway,
|
||||
service_unavailable,
|
||||
gateway_timeout,
|
||||
http_version_not_supported,
|
||||
|
||||
// RFC 7232.
|
||||
//
|
||||
not_modified,
|
||||
precondition_failed,
|
||||
|
||||
// RFC 7235.
|
||||
//
|
||||
unauthorized,
|
||||
proxy_auth_required,
|
||||
|
||||
// Unofficial.
|
||||
//
|
||||
enhance_your_calm
|
||||
|
||||
};
|
||||
|
||||
} // namespace code::seafire::server
|
||||
|
||||
#endif
|
21
code/seafire/server/configuration.hxx
Normal file
21
code/seafire/server/configuration.hxx
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef code__seafire__server__configuration_hxx_
|
||||
#define code__seafire__server__configuration_hxx_
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
/// Holds server configuration parameters.
|
||||
///
|
||||
struct configuration_t
|
||||
{
|
||||
/// The timeout of a transaction.
|
||||
///
|
||||
std::chrono::seconds request_timeout;
|
||||
|
||||
};
|
||||
|
||||
} // namespace code::seafire::server
|
||||
|
||||
#endif
|
33
code/seafire/server/counter.hxx
Normal file
33
code/seafire/server/counter.hxx
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef code__seafire__server__counter_hxx_
|
||||
#define code__seafire__server__counter_hxx_
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
class atomic_t
|
||||
: public std::atomic<T>
|
||||
{
|
||||
public:
|
||||
atomic_t()
|
||||
: std::atomic<T>{0}
|
||||
{}
|
||||
|
||||
atomic_t(atomic_t<T> const& other)
|
||||
: std::atomic<T>{other.load()}
|
||||
{}
|
||||
|
||||
atomic_t(atomic_t<T>&& other)
|
||||
: std::atomic<T>{other.load()}
|
||||
{}
|
||||
|
||||
};
|
||||
|
||||
using counter_t = atomic_t<std::uint64_t>;
|
||||
|
||||
} // namespace code::seafire::server
|
||||
|
||||
#endif
|
41
code/seafire/server/diagnostics.cxx
Normal file
41
code/seafire/server/diagnostics.cxx
Normal file
@ -0,0 +1,41 @@
|
||||
#include <code/seafire/server/diagnostics.hxx>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
common::diagnostics_t::category_t const&
|
||||
server_category()
|
||||
{
|
||||
static common::diagnostics_t::category_t category{"server"};
|
||||
return category;
|
||||
}
|
||||
|
||||
common::diagnostics_t::category_t const&
|
||||
supervisor_category()
|
||||
{
|
||||
static common::diagnostics_t::category_t category{"supervisor"};
|
||||
return category;
|
||||
}
|
||||
|
||||
common::diagnostics_t::category_t const&
|
||||
session_category()
|
||||
{
|
||||
static common::diagnostics_t::category_t category{"session"};
|
||||
return category;
|
||||
}
|
||||
|
||||
common::diagnostics_t::category_t const&
|
||||
transaction_category()
|
||||
{
|
||||
static common::diagnostics_t::category_t category{"transaction"};
|
||||
return category;
|
||||
}
|
||||
|
||||
common::diagnostics_t::category_t const&
|
||||
request_category()
|
||||
{
|
||||
static common::diagnostics_t::category_t category{"request"};
|
||||
return category;
|
||||
}
|
||||
|
||||
} // namespace code::seafire::server
|
26
code/seafire/server/diagnostics.hxx
Normal file
26
code/seafire/server/diagnostics.hxx
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef code__seafire__server__diagnostics_hxx_
|
||||
#define code__seafire__server__diagnostics_hxx_
|
||||
|
||||
#include <code/seafire/common/diagnostics.hxx>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
common::diagnostics_t::category_t const&
|
||||
server_category();
|
||||
|
||||
common::diagnostics_t::category_t const&
|
||||
supervisor_category();
|
||||
|
||||
common::diagnostics_t::category_t const&
|
||||
session_category();
|
||||
|
||||
common::diagnostics_t::category_t const&
|
||||
transaction_category();
|
||||
|
||||
common::diagnostics_t::category_t const&
|
||||
request_category();
|
||||
|
||||
} // namespace code::seafire::server
|
||||
|
||||
#endif
|
12
code/seafire/server/error-handler.cxx
Normal file
12
code/seafire/server/error-handler.cxx
Normal file
@ -0,0 +1,12 @@
|
||||
#include <code/seafire/server/error-handler.hxx>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
error_handler_t::
|
||||
error_handler_t() = default;
|
||||
|
||||
error_handler_t::
|
||||
~error_handler_t() noexcept = default;
|
||||
|
||||
} // namespace code::seafire::serverk
|
38
code/seafire/server/error-handler.hxx
Normal file
38
code/seafire/server/error-handler.hxx
Normal file
@ -0,0 +1,38 @@
|
||||
#ifndef code__seafire__server__error_handler_hxx_
|
||||
#define code__seafire__server__error_handler_hxx_
|
||||
|
||||
#include <code/seafire/server/common-error.hxx>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
class request_t;
|
||||
class response_t;
|
||||
|
||||
class error_handler_t
|
||||
{
|
||||
public:
|
||||
virtual
|
||||
void
|
||||
on_error(request_t&, response_t&, common_error_t) = 0;
|
||||
|
||||
virtual
|
||||
void
|
||||
on_exception(request_t&, response_t&) noexcept = 0;
|
||||
|
||||
protected:
|
||||
error_handler_t();
|
||||
|
||||
error_handler_t(error_handler_t const&) = delete;
|
||||
error_handler_t(error_handler_t&&) = delete;
|
||||
|
||||
~error_handler_t() noexcept;
|
||||
|
||||
error_handler_t& operator=(error_handler_t const&) = delete;
|
||||
error_handler_t& operator=(error_handler_t&&) = delete;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
44
code/seafire/server/middleware.cxx
Normal file
44
code/seafire/server/middleware.cxx
Normal file
@ -0,0 +1,44 @@
|
||||
#include <code/seafire/server/middleware.hxx>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
void
|
||||
middleware_t::
|
||||
invoke(request_t& req, response_t& res, request_handler_t const& next) const
|
||||
{
|
||||
handler_->invoke(req, res, next);
|
||||
}
|
||||
|
||||
middleware_t::
|
||||
middleware_t(std::shared_ptr<concept_t const> handler)
|
||||
: handler_{handler}
|
||||
{}
|
||||
|
||||
request_handler_t
|
||||
make_middleware(std::vector<middleware_t> const& chain,
|
||||
request_handler_t handler)
|
||||
{
|
||||
struct handler_t
|
||||
{
|
||||
middleware_t middleware;
|
||||
request_handler_t next;
|
||||
|
||||
void
|
||||
operator()(request_t& req, response_t& res) const
|
||||
{
|
||||
middleware.invoke(req, res, next);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
request_handler_t next{std::move(handler)};
|
||||
|
||||
for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
|
||||
next = handler_t{*it, next};
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
} // namespace code::seafire::server
|
84
code/seafire/server/middleware.hxx
Normal file
84
code/seafire/server/middleware.hxx
Normal file
@ -0,0 +1,84 @@
|
||||
#ifndef code__seafire__server__middleware_hxx_
|
||||
#define code__seafire__server__middleware_hxx_
|
||||
|
||||
#include <code/seafire/server/request-handler.hxx>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
/// Implements middleware functionality.
|
||||
///
|
||||
class middleware_t
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
middleware_t(T handler)
|
||||
: handler_{std::make_shared<container_t<T>>(std::move(handler))}
|
||||
{}
|
||||
|
||||
void
|
||||
invoke(request_t&, response_t&, request_handler_t const&) const;
|
||||
|
||||
template<typename, typename... Args>
|
||||
friend
|
||||
middleware_t
|
||||
make_middleware(Args&&...);
|
||||
|
||||
private:
|
||||
struct concept_t
|
||||
{
|
||||
virtual
|
||||
~concept_t() noexcept = default;
|
||||
|
||||
virtual
|
||||
void
|
||||
invoke(request_t&, response_t&, request_handler_t const&) const = 0;
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct container_t
|
||||
: concept_t
|
||||
{
|
||||
template<typename... Args>
|
||||
container_t(Args&&... args)
|
||||
: handler{std::forward<Args>(args)...}
|
||||
{}
|
||||
|
||||
void
|
||||
invoke(request_t& req,
|
||||
response_t& res,
|
||||
request_handler_t const& next) const override
|
||||
{
|
||||
handler(req, res, next);
|
||||
}
|
||||
|
||||
T handler;
|
||||
|
||||
};
|
||||
|
||||
explicit
|
||||
middleware_t(std::shared_ptr<concept_t const>);
|
||||
|
||||
std::shared_ptr<concept_t const> handler_;
|
||||
|
||||
};
|
||||
|
||||
template<typename T, typename... Args>
|
||||
middleware_t
|
||||
make_middleware(Args&&... args)
|
||||
{
|
||||
return middleware_t{
|
||||
std::make_shared<middleware_t::container_t<T>>(std::forward<Args>(args)...)
|
||||
};
|
||||
}
|
||||
|
||||
request_handler_t
|
||||
make_middleware(std::vector<middleware_t> const&, request_handler_t);
|
||||
|
||||
} // namespace code::seafire::server
|
||||
|
||||
#endif
|
45
code/seafire/server/parameters.cxx
Normal file
45
code/seafire/server/parameters.cxx
Normal file
@ -0,0 +1,45 @@
|
||||
#include <code/seafire/server/parameters.hxx>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
std::optional<string_parameter_t::value_type>
|
||||
string_parameter_t::
|
||||
try_parse(std::optional<std::string> const& input)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
std::optional<int_parameter_t::value_type>
|
||||
int_parameter_t::
|
||||
try_parse(std::optional<std::string> const& input)
|
||||
{
|
||||
try {
|
||||
if (input) {
|
||||
return std::stoll(*input);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
catch (...) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<uint_parameter_t::value_type>
|
||||
uint_parameter_t::
|
||||
try_parse(std::optional<std::string> const& input)
|
||||
{
|
||||
try {
|
||||
if (input) {
|
||||
return std::stoull(*input);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
catch (...) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace code::seafire::server
|
62
code/seafire/server/parameters.hxx
Normal file
62
code/seafire/server/parameters.hxx
Normal file
@ -0,0 +1,62 @@
|
||||
#ifndef code__seafire__server__parameters_hxx_
|
||||
#define code__seafire__server__parameters_hxx_
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
template<std::size_t N>
|
||||
struct parameter_name_t
|
||||
{
|
||||
constexpr parameter_name_t(const char (&str)[N])
|
||||
{
|
||||
std::copy_n(str, N, name);
|
||||
}
|
||||
|
||||
operator std::string const() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
char name[N];
|
||||
|
||||
};
|
||||
|
||||
struct string_parameter_t
|
||||
{
|
||||
using value_type = std::string;
|
||||
|
||||
static
|
||||
std::optional<value_type>
|
||||
try_parse(std::optional<std::string> const&);
|
||||
|
||||
};
|
||||
|
||||
struct int_parameter_t
|
||||
{
|
||||
using value_type = std::int64_t;
|
||||
|
||||
static
|
||||
std::optional<value_type>
|
||||
try_parse(std::optional<std::string> const&);
|
||||
|
||||
};
|
||||
|
||||
struct uint_parameter_t
|
||||
{
|
||||
using value_type = std::uint64_t;
|
||||
|
||||
static
|
||||
std::optional<value_type>
|
||||
try_parse(std::optional<std::string> const&);
|
||||
|
||||
};
|
||||
|
||||
} // namespace code::seafire::server
|
||||
|
||||
#endif
|
64
code/seafire/server/query-parameter.hxx
Normal file
64
code/seafire/server/query-parameter.hxx
Normal file
@ -0,0 +1,64 @@
|
||||
#ifndef code__seafire__server__query_parameter_hxx_
|
||||
#define code__seafire__server__query_parameter_hxx_
|
||||
|
||||
#include <code/seafire/server/parameters.hxx>
|
||||
#include <code/seafire/server/query-parameters.hxx>
|
||||
#include <code/seafire/server/request.hxx>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
template<parameter_name_t Name, typename ParameterType = string_parameter_t>
|
||||
class query_parameter_t
|
||||
{
|
||||
public:
|
||||
using parameter_type = ParameterType;
|
||||
using value_type = typename parameter_type::value_type;
|
||||
|
||||
static
|
||||
std::string const&
|
||||
name()
|
||||
{
|
||||
static std::string const name{Name};
|
||||
return name;
|
||||
}
|
||||
|
||||
query_parameter_t(std::optional<value_type> value)
|
||||
: value_{std::move(value)}
|
||||
{}
|
||||
|
||||
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
|
||||
query_parameter_t<Name, ParameterType>
|
||||
fetch(request_t& r)
|
||||
{
|
||||
auto value = r.extensions().use<query_parameters_t>().get(name());
|
||||
return std::optional<value_type>{parameter_type::try_parse(value)};
|
||||
}
|
||||
|
||||
private:
|
||||
std::optional<value_type> value_;
|
||||
|
||||
};
|
||||
|
||||
} // namespace code::seafire::server
|
||||
|
||||
#endif
|
61
code/seafire/server/query-parameters.cxx
Normal file
61
code/seafire/server/query-parameters.cxx
Normal file
@ -0,0 +1,61 @@
|
||||
#include <code/seafire/server/query-parameters.hxx>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
query_parameters_t::
|
||||
query_parameters_t(std::map<std::string, std::string> keys)
|
||||
: keys_{std::move(keys)}
|
||||
{}
|
||||
|
||||
std::optional<std::string>
|
||||
query_parameters_t::
|
||||
get(std::string const& key) const
|
||||
{
|
||||
if (auto it = keys_.find(key); it != keys_.end())
|
||||
return it->second;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<query_parameters_t>
|
||||
query_parameters_t::
|
||||
try_parse(std::string const& q)
|
||||
{
|
||||
std::map<std::string, std::string> keys;
|
||||
|
||||
for (auto it = q.begin(); it != q.end();) {
|
||||
auto k = it;
|
||||
|
||||
while (it != q.end() && *it != '=' && *it != '&') {
|
||||
++it;
|
||||
}
|
||||
|
||||
auto kend = it;
|
||||
|
||||
if (it != q.end() && *it == '=') {
|
||||
++it; // skips '='
|
||||
}
|
||||
|
||||
auto v = it;
|
||||
|
||||
while (it != q.end() && *it != '&') {
|
||||
++it;
|
||||
}
|
||||
|
||||
auto vend = it;
|
||||
|
||||
if (it != q.end() && *it == '&') {
|
||||
++it; // skips '&'
|
||||
}
|
||||
|
||||
std::string key{k, kend};
|
||||
std::string value{v, vend};
|
||||
|
||||
keys.emplace(std::move(key), std::move(value));
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
} // namespace code::seafire::server
|
30
code/seafire/server/query-parameters.hxx
Normal file
30
code/seafire/server/query-parameters.hxx
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef code__seafire__server__query_parameters_hxx_
|
||||
#define code__seafire__server__query_parameters_hxx_
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
class query_parameters_t
|
||||
{
|
||||
public:
|
||||
query_parameters_t(std::map<std::string, std::string>);
|
||||
|
||||
std::optional<std::string>
|
||||
get(std::string const&) const;
|
||||
|
||||
static
|
||||
std::optional<query_parameters_t>
|
||||
try_parse(std::string const&);
|
||||
|
||||
private:
|
||||
std::map<std::string, std::string> keys_;
|
||||
|
||||
};
|
||||
|
||||
} // namespace code::seafire::server
|
||||
|
||||
#endif
|
13
code/seafire/server/request-handler.cxx
Normal file
13
code/seafire/server/request-handler.cxx
Normal file
@ -0,0 +1,13 @@
|
||||
#include <code/seafire/server/request-handler.hxx>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
void
|
||||
request_handler_t::
|
||||
invoke(request_t& req, response_t& res) const
|
||||
{
|
||||
handler_->invoke(req, res);
|
||||
}
|
||||
|
||||
} // namespace code::seafire::server
|
82
code/seafire/server/request-handler.hxx
Normal file
82
code/seafire/server/request-handler.hxx
Normal file
@ -0,0 +1,82 @@
|
||||
#ifndef code__seafire__server__request_handler_hxx_
|
||||
#define code__seafire__server__request_handler_hxx_
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
class request_t;
|
||||
class response_t;
|
||||
|
||||
class request_handler_t
|
||||
{
|
||||
public:
|
||||
template<typename Handler>
|
||||
request_handler_t(Handler handler)
|
||||
: handler_{
|
||||
std::make_shared<container_t<Handler>>(
|
||||
std::move(handler)
|
||||
)
|
||||
}
|
||||
{}
|
||||
|
||||
void
|
||||
invoke(request_t& req, response_t& res) const;
|
||||
|
||||
template<typename Handler, typename... Args>
|
||||
friend
|
||||
request_handler_t
|
||||
make_request_handler(Args&&...);
|
||||
|
||||
private:
|
||||
struct concept_t
|
||||
{
|
||||
virtual
|
||||
~concept_t() noexcept = default;
|
||||
|
||||
virtual
|
||||
void
|
||||
invoke(request_t&, response_t&) const = 0;
|
||||
|
||||
};
|
||||
|
||||
template<typename Handler>
|
||||
struct container_t
|
||||
: concept_t
|
||||
{
|
||||
template<typename... Args>
|
||||
container_t(Args&&... args)
|
||||
: handler_{std::forward<Args>(args)...}
|
||||
{}
|
||||
|
||||
void
|
||||
invoke(request_t& req, response_t& res) const override
|
||||
{
|
||||
handler_(req, res);
|
||||
}
|
||||
|
||||
Handler handler_;
|
||||
};
|
||||
|
||||
request_handler_t(std::shared_ptr<concept_t> handler)
|
||||
: handler_{std::move(handler)}
|
||||
{}
|
||||
|
||||
// Request handlers are shared for performance reasons and hence const.
|
||||
//
|
||||
std::shared_ptr<concept_t const> handler_;
|
||||
|
||||
};
|
||||
|
||||
template<typename Handler, typename... Args>
|
||||
request_handler_t
|
||||
make_request_handler(Args&&... args)
|
||||
{
|
||||
using container_t = request_handler_t::container_t<Handler>;
|
||||
return std::make_shared<container_t>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // namespace code::seafire::server
|
||||
|
||||
#endif
|
35
code/seafire/server/request.cxx
Normal file
35
code/seafire/server/request.cxx
Normal file
@ -0,0 +1,35 @@
|
||||
#include <code/seafire/server/request.hxx>
|
||||
|
||||
#include <code/seafire/protocol/rfc7230/connection.hxx>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
protocol::request_t const&
|
||||
request_t::
|
||||
get_message() const
|
||||
{
|
||||
return get_request();
|
||||
}
|
||||
|
||||
std::istream&
|
||||
request_t::
|
||||
content()
|
||||
{
|
||||
return get_request_content();
|
||||
}
|
||||
|
||||
common::extension_context_t&
|
||||
request_t::
|
||||
extensions()
|
||||
{
|
||||
return get_request_extensions();
|
||||
}
|
||||
|
||||
request_t::
|
||||
request_t() = default;
|
||||
|
||||
request_t::
|
||||
~request_t() noexcept = default;
|
||||
|
||||
} // namespace code::seafire::server
|
83
code/seafire/server/request.hxx
Normal file
83
code/seafire/server/request.hxx
Normal file
@ -0,0 +1,83 @@
|
||||
#ifndef code__seafire__server__request_hxx_
|
||||
#define code__seafire__server__request_hxx_
|
||||
|
||||
#include <code/seafire/protocol/request.hxx>
|
||||
|
||||
#include <code/seafire/common/allocator.hxx>
|
||||
#include <code/seafire/common/extension-context.hxx>
|
||||
|
||||
#include <code/seafire/common/io/buffer.hxx>
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
/// Represents a server-side request.
|
||||
///
|
||||
class request_t
|
||||
{
|
||||
public:
|
||||
protocol::request_t const&
|
||||
get_message() const;
|
||||
|
||||
std::istream&
|
||||
content();
|
||||
|
||||
// fixme: replace with non-virtual function and virtual
|
||||
// get_request_allocator function.
|
||||
//
|
||||
virtual
|
||||
common::allocator_t&
|
||||
memory() = 0;
|
||||
|
||||
common::extension_context_t&
|
||||
extensions();
|
||||
|
||||
protected:
|
||||
request_t();
|
||||
|
||||
request_t(request_t const&) = delete;
|
||||
request_t(request_t&&) = delete;
|
||||
|
||||
~request_t() noexcept;
|
||||
|
||||
request_t& operator=(request_t const&) = delete;
|
||||
request_t& operator=(request_t&&) = delete;
|
||||
|
||||
private:
|
||||
virtual
|
||||
protocol::request_t const&
|
||||
get_request() const = 0;
|
||||
|
||||
virtual
|
||||
std::istream&
|
||||
get_request_content() = 0;
|
||||
|
||||
virtual
|
||||
common::extension_context_t&
|
||||
get_request_extensions() = 0;
|
||||
|
||||
};
|
||||
|
||||
template<typename Header>
|
||||
bool
|
||||
has(request_t const&);
|
||||
|
||||
template<typename Header>
|
||||
bool
|
||||
has_quick(request_t const&);
|
||||
|
||||
template<typename Header>
|
||||
auto
|
||||
get(request_t const&);
|
||||
|
||||
template<typename Header>
|
||||
auto
|
||||
get(request_t const&, std::error_code&);
|
||||
|
||||
} // namespace code::seafire::server
|
||||
|
||||
#include <code/seafire/server/request.txx>
|
||||
|
||||
#endif
|
32
code/seafire/server/request.txx
Normal file
32
code/seafire/server/request.txx
Normal file
@ -0,0 +1,32 @@
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
template<typename Header>
|
||||
bool
|
||||
has(request_t const& r)
|
||||
{
|
||||
return protocol::has<Header>(r.get_message());
|
||||
}
|
||||
|
||||
template<typename Header>
|
||||
bool
|
||||
has_quick(request_t const& r)
|
||||
{
|
||||
return protocol::has_quick<Header>(r.get_message());
|
||||
}
|
||||
|
||||
template<typename Header>
|
||||
auto
|
||||
get(request_t const& r)
|
||||
{
|
||||
return protocol::get<Header>(r.get_message());
|
||||
}
|
||||
|
||||
template<typename Header>
|
||||
auto
|
||||
get(request_t const& r, std::error_code& ec)
|
||||
{
|
||||
return protocol::get<Header>(r.get_message(), ec);
|
||||
}
|
||||
|
||||
} // namespace code::seafire::server
|
146
code/seafire/server/response.cxx
Normal file
146
code/seafire/server/response.cxx
Normal file
@ -0,0 +1,146 @@
|
||||
#include <code/seafire/server/response.hxx>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
/// Access the response message.
|
||||
///
|
||||
protocol::response_t&
|
||||
response_t::
|
||||
get_message()
|
||||
{
|
||||
return get_response();
|
||||
}
|
||||
|
||||
/// Access the response message.
|
||||
///
|
||||
protocol::response_t const&
|
||||
response_t::
|
||||
get_message() const
|
||||
{
|
||||
return get_response();
|
||||
}
|
||||
|
||||
/// Send response.
|
||||
///
|
||||
void
|
||||
response_t::
|
||||
send(protocol::status_code_t s)
|
||||
{
|
||||
send(s, common::io::const_buffers_t{});
|
||||
}
|
||||
|
||||
/// Send response.
|
||||
///
|
||||
void
|
||||
response_t::
|
||||
send(protocol::status_code_t s, common::io::const_buffer_t const& content)
|
||||
{
|
||||
send(s, common::io::const_buffers_t{content});
|
||||
}
|
||||
|
||||
/// Send response.
|
||||
///
|
||||
void
|
||||
response_t::
|
||||
send(protocol::status_code_t s, common::io::const_buffers_t const& content)
|
||||
{
|
||||
do_send_response(s, content);
|
||||
}
|
||||
|
||||
/// Send response.
|
||||
///
|
||||
void
|
||||
response_t::
|
||||
send(protocol::status_code_t s, stream_t const& content)
|
||||
{
|
||||
send(s, content.rdbuf()->data());
|
||||
}
|
||||
|
||||
/// Send error.
|
||||
///
|
||||
void
|
||||
response_t::
|
||||
send(common_error_t error)
|
||||
{
|
||||
do_send_error(error);
|
||||
}
|
||||
|
||||
/// Allocate a new stream.
|
||||
///
|
||||
response_t::stream_t
|
||||
response_t::
|
||||
allocate_stream()
|
||||
{
|
||||
return stream_t{&memory().alloc_emplace<asio::streambuf>()};
|
||||
}
|
||||
|
||||
/// Access response extensions.
|
||||
///
|
||||
common::extension_context_t&
|
||||
response_t::
|
||||
extensions()
|
||||
{
|
||||
return get_response_extensions();
|
||||
}
|
||||
|
||||
void
|
||||
response_t::
|
||||
invoke_finalizer(finalizer_t* f, request_t& r)
|
||||
{
|
||||
if (f) {
|
||||
f->invoke(r, *this);
|
||||
}
|
||||
}
|
||||
|
||||
response_t::
|
||||
response_t() = default;
|
||||
|
||||
response_t::
|
||||
~response_t() noexcept = default;
|
||||
|
||||
asio::streambuf*
|
||||
response_t::stream_t::
|
||||
rdbuf() const
|
||||
{
|
||||
return rdbuf_;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
response_t::stream_t::
|
||||
size() const
|
||||
{
|
||||
return rdbuf_->data().size();
|
||||
}
|
||||
|
||||
response_t::stream_t::
|
||||
stream_t(asio::streambuf* rdbuf)
|
||||
: std::iostream{rdbuf ? rdbuf : throw std::invalid_argument{"buf"}},
|
||||
rdbuf_{rdbuf}
|
||||
{}
|
||||
|
||||
/// Construct a new finalizer.
|
||||
///
|
||||
response_t::finalizer_t::
|
||||
finalizer_t(response_t& r, function_t f)
|
||||
: response_{r}, f_{f}
|
||||
{
|
||||
response_.register_finalizer(this);
|
||||
}
|
||||
|
||||
/// Destroy this finalizer.
|
||||
///
|
||||
response_t::finalizer_t::
|
||||
~finalizer_t() noexcept
|
||||
{
|
||||
response_.deregister_finalizer(this);
|
||||
}
|
||||
|
||||
void
|
||||
response_t::finalizer_t::
|
||||
invoke(request_t& req, response_t& res)
|
||||
{
|
||||
f_(req, res);
|
||||
}
|
||||
|
||||
} // namespace code::seafire::server
|
209
code/seafire/server/response.hxx
Normal file
209
code/seafire/server/response.hxx
Normal file
@ -0,0 +1,209 @@
|
||||
#ifndef code__seafire__server__response_hxx_
|
||||
#define code__seafire__server__response_hxx_
|
||||
|
||||
#include <code/seafire/server/common-error.hxx>
|
||||
|
||||
#include <code/seafire/protocol/response.hxx>
|
||||
#include <code/seafire/protocol/status-code.hxx>
|
||||
|
||||
#include <code/seafire/common/allocator.hxx>
|
||||
#include <code/seafire/common/extension-context.hxx>
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
class request_t;
|
||||
|
||||
/// Represents a server-side response.
|
||||
///
|
||||
class response_t
|
||||
{
|
||||
public:
|
||||
class stream_t;
|
||||
class finalizer_t;
|
||||
|
||||
friend finalizer_t;
|
||||
|
||||
protocol::response_t&
|
||||
get_message();
|
||||
|
||||
protocol::response_t const&
|
||||
get_message() const;
|
||||
|
||||
void
|
||||
send(protocol::status_code_t);
|
||||
|
||||
void
|
||||
send(protocol::status_code_t, common::io::const_buffer_t const&);
|
||||
|
||||
void
|
||||
send(protocol::status_code_t, common::io::const_buffers_t const&);
|
||||
|
||||
void
|
||||
send(protocol::status_code_t, stream_t const&);
|
||||
|
||||
void
|
||||
send(common_error_t);
|
||||
|
||||
stream_t
|
||||
allocate_stream();
|
||||
|
||||
// fixme: replace with non-virtual function and virtual
|
||||
// get_response_allocator function.
|
||||
//
|
||||
virtual
|
||||
common::allocator_t&
|
||||
memory() = 0;
|
||||
|
||||
common::extension_context_t&
|
||||
extensions();
|
||||
|
||||
protected:
|
||||
response_t();
|
||||
|
||||
response_t(response_t const&) = delete;
|
||||
response_t(response_t&&) = delete;
|
||||
|
||||
~response_t() noexcept;
|
||||
|
||||
virtual
|
||||
void
|
||||
register_finalizer(finalizer_t*) = 0;
|
||||
|
||||
virtual
|
||||
void
|
||||
deregister_finalizer(finalizer_t*) = 0;
|
||||
|
||||
void
|
||||
invoke_finalizer(finalizer_t*, request_t&);
|
||||
|
||||
response_t& operator=(response_t const&) = delete;
|
||||
response_t& operator=(response_t&&) = delete;
|
||||
|
||||
private:
|
||||
virtual
|
||||
protocol::response_t&
|
||||
get_response() = 0;
|
||||
|
||||
virtual
|
||||
protocol::response_t const&
|
||||
get_response() const = 0;
|
||||
|
||||
virtual
|
||||
common::extension_context_t&
|
||||
get_response_extensions() = 0;
|
||||
|
||||
virtual
|
||||
void
|
||||
do_send_response(protocol::status_code_t,
|
||||
common::io::const_buffers_t const&) = 0;
|
||||
|
||||
virtual
|
||||
void
|
||||
do_send_error(common_error_t) = 0;
|
||||
|
||||
};
|
||||
|
||||
template<typename Header>
|
||||
bool
|
||||
has(response_t const& r)
|
||||
{
|
||||
return protocol::has<Header>(r.get_message());
|
||||
}
|
||||
|
||||
template<typename Header>
|
||||
bool
|
||||
has_quick(response_t const& r)
|
||||
{
|
||||
return protocol::has_quick<Header>(r.get_message());
|
||||
}
|
||||
|
||||
template<typename Header>
|
||||
auto
|
||||
get(response_t const& r)
|
||||
{
|
||||
return protocol::get<Header>(r.get_message());
|
||||
}
|
||||
|
||||
template<typename Header>
|
||||
auto
|
||||
get(response_t const& r, std::error_code& ec)
|
||||
{
|
||||
return protocol::get<Header>(r.get_message(), ec);
|
||||
}
|
||||
|
||||
template<typename Header, typename... Args>
|
||||
void
|
||||
set(response_t& r, Args&&... args)
|
||||
{
|
||||
protocol::set<Header>(r.get_message(), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename Header>
|
||||
void
|
||||
erase(response_t& r)
|
||||
{
|
||||
protocol::erase<Header>(r.get_message());
|
||||
}
|
||||
|
||||
template<typename Header, typename... Args>
|
||||
void
|
||||
set_if_not_set(response_t& r, Args&&... args)
|
||||
{
|
||||
protocol::set_if_not_set<Header>(r.get_message(), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
class response_t::stream_t
|
||||
: public std::iostream
|
||||
{
|
||||
public:
|
||||
asio::streambuf*
|
||||
rdbuf() const;
|
||||
|
||||
std::size_t
|
||||
size() const;
|
||||
|
||||
private:
|
||||
friend response_t;
|
||||
|
||||
explicit
|
||||
stream_t(asio::streambuf*);
|
||||
|
||||
asio::streambuf* rdbuf_;
|
||||
|
||||
};
|
||||
|
||||
class response_t::finalizer_t
|
||||
{
|
||||
public:
|
||||
using function_t = std::function<void(request_t&, response_t&)>;
|
||||
|
||||
finalizer_t(response_t&, function_t);
|
||||
|
||||
finalizer_t(finalizer_t const&) = delete;
|
||||
finalizer_t(finalizer_t&&) = delete;
|
||||
|
||||
~finalizer_t() noexcept;
|
||||
|
||||
finalizer_t& operator=(finalizer_t const&) = delete;
|
||||
finalizer_t& operator=(finalizer_t&&) = delete;
|
||||
|
||||
private:
|
||||
friend response_t;
|
||||
|
||||
void
|
||||
invoke(request_t&, response_t&);
|
||||
|
||||
response_t& response_;
|
||||
function_t f_;
|
||||
|
||||
};
|
||||
|
||||
} // namespace code::seafire::server
|
||||
|
||||
#endif
|
331
code/seafire/server/server.cxx
Normal file
331
code/seafire/server/server.cxx
Normal file
@ -0,0 +1,331 @@
|
||||
#include <code/seafire/server/diagnostics.hxx>
|
||||
#include <code/seafire/server/response.hxx>
|
||||
#include <code/seafire/server/server.hxx>
|
||||
|
||||
#include <code/seafire/protocol/rfc7231/content-type.hxx>
|
||||
|
||||
#include <code/seafire/common/io/buffer.hxx>
|
||||
#include <code/seafire/common/io/error.hxx>
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
/// Construct a new HTTP server using the specified acceptors
|
||||
/// and request handler.
|
||||
///
|
||||
server_t::
|
||||
server_t(common::diagnostics_t& diagnostics,
|
||||
configuration_t configuration,
|
||||
acceptor_set_t acceptors,
|
||||
request_handler_t handler)
|
||||
: diagnostics_{diagnostics},
|
||||
configuration_{configuration},
|
||||
acceptors_{std::move(acceptors)},
|
||||
handler_{handler},
|
||||
supervisor_{diagnostics_, *this, handler_}
|
||||
{}
|
||||
|
||||
void
|
||||
server_t::
|
||||
start()
|
||||
{
|
||||
trace() << "starting seafire server...";
|
||||
|
||||
// fixme: add tracing.
|
||||
//
|
||||
|
||||
for (auto const& j : acceptors_) {
|
||||
init_accept(*j);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
server_t::
|
||||
stop(bool quick)
|
||||
{
|
||||
trace() << "seafire server stop requested...";
|
||||
|
||||
if (quick) {
|
||||
supervisor_.stop_all();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
server_t::
|
||||
on_error(request_t& req, response_t& res, common_error_t error)
|
||||
{
|
||||
trace() << "on_error()...";
|
||||
|
||||
namespace rfc7231 = protocol::rfc7231;
|
||||
|
||||
switch (error) {
|
||||
case common_error_t::bad_request: {
|
||||
static std::string const message{"Bad request\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(400, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
case common_error_t::payment_required: {
|
||||
static std::string const message{"Payment required\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(402, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
case common_error_t::forbidden: {
|
||||
static std::string const message{"Forbidden\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(403, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
case common_error_t::not_found: {
|
||||
static std::string const message{"Not found\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(404, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
case common_error_t::method_not_allowed: {
|
||||
static std::string const message{"Method not allowed\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(405, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
case common_error_t::not_acceptable: {
|
||||
static std::string const message{"Not acceptable\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(406, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
case common_error_t::request_timeout: {
|
||||
static std::string const message{"Request timeout\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(408, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
case common_error_t::conflict: {
|
||||
static std::string const message{"Conflict\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(409, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
case common_error_t::gone: {
|
||||
static std::string const message{"Gone\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(410, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
case common_error_t::length_required: {
|
||||
static std::string const message{"Length required\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(411, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
case common_error_t::payload_too_large: {
|
||||
static std::string const message{"Payload too large\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(413, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
case common_error_t::uri_too_long: {
|
||||
static std::string const message{"Target URI too long\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(414, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
case common_error_t::unsupported_media_type: {
|
||||
static std::string const message{"Unsupported media type\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(415, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
case common_error_t::expectation_failed: {
|
||||
static std::string const message{"Expectation failed\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(417, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
case common_error_t::upgrade_required: {
|
||||
static std::string const message{"Upgrade required\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(426, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
case common_error_t::internal_server_error: {
|
||||
static std::string const message{"Internal server error\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(500, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
case common_error_t::not_implemented: {
|
||||
static std::string const message{"Not implemented\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(501, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
case common_error_t::bad_gateway: {
|
||||
static std::string const message{"Bad gateway\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(502, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
case common_error_t::service_unavailable: {
|
||||
static std::string const message{"Service unavailable\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(503, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
case common_error_t::gateway_timeout: {
|
||||
static std::string const message{"Gateway timeout\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(504, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
case common_error_t::http_version_not_supported: {
|
||||
static std::string const message{"HTTP version not supported\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(505, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
// rfc 7232
|
||||
//
|
||||
|
||||
case common_error_t::not_modified: {
|
||||
static std::string const message{"Not modified\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(304, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
case common_error_t::precondition_failed: {
|
||||
static std::string const message{"Precondition failed\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(412, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
// rfc 7235
|
||||
//
|
||||
|
||||
case common_error_t::unauthorized: {
|
||||
static std::string const message{"Unauthorized\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(401, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
case common_error_t::proxy_auth_required: {
|
||||
static std::string const message{"Proxy authentication required\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(407, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
|
||||
// Unofficial.
|
||||
//
|
||||
|
||||
case common_error_t::enhance_your_calm: {
|
||||
static std::string const message{"Enhance your calm\n"};
|
||||
|
||||
set<rfc7231::content_type_t>(res, "text", "plain");
|
||||
res.send(420, common::io::buffer(message));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw std::invalid_argument{"invalid common error"};
|
||||
}
|
||||
|
||||
void
|
||||
server_t::
|
||||
on_exception(request_t&, response_t&) noexcept
|
||||
{
|
||||
trace() << "on_exception()...";
|
||||
}
|
||||
|
||||
common::diagnostics_t::proxy_t
|
||||
server_t::
|
||||
trace()
|
||||
{
|
||||
return diagnostics_ << server_category();
|
||||
}
|
||||
|
||||
void
|
||||
server_t::
|
||||
init_accept(common::io::acceptor_t& acceptor)
|
||||
{
|
||||
trace() << "init_accept()...";
|
||||
|
||||
auto bound = [this, &acceptor](std::error_code const& ec)
|
||||
{
|
||||
on_accept(acceptor, ec);
|
||||
};
|
||||
|
||||
supervisor_.async_accept(acceptor, bound);
|
||||
}
|
||||
|
||||
void
|
||||
server_t::
|
||||
on_accept(common::io::acceptor_t& acceptor, std::error_code const& ec)
|
||||
{
|
||||
trace() << "on_accept()...";
|
||||
|
||||
if (!ec) {
|
||||
// no error.
|
||||
//
|
||||
init_accept(acceptor);
|
||||
}
|
||||
else if (ec != asio::error::operation_aborted) {
|
||||
// fixme: handle error accordingly.
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace code::seafire::server
|
75
code/seafire/server/server.hxx
Normal file
75
code/seafire/server/server.hxx
Normal file
@ -0,0 +1,75 @@
|
||||
#ifndef code__seafire__server__hxx_
|
||||
#define code__seafire__server__hxx_
|
||||
|
||||
#include <code/seafire/server/common-error.hxx>
|
||||
#include <code/seafire/server/configuration.hxx>
|
||||
#include <code/seafire/server/counter.hxx>
|
||||
#include <code/seafire/server/request-handler.hxx>
|
||||
#include <code/seafire/server/supervisor.hxx>
|
||||
|
||||
#include <code/seafire/common/diagnostics.hxx>
|
||||
|
||||
#include <code/seafire/common/io/acceptor.hxx>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
class request_t;
|
||||
class response_t;
|
||||
class transaction_t;
|
||||
|
||||
class server_t
|
||||
: public error_handler_t
|
||||
{
|
||||
public:
|
||||
/// Acceptor set type.
|
||||
///
|
||||
using acceptor_set_t = std::set<std::unique_ptr<common::io::acceptor_t>>;
|
||||
|
||||
server_t(common::diagnostics_t&,
|
||||
configuration_t,
|
||||
acceptor_set_t,
|
||||
request_handler_t);
|
||||
|
||||
server_t(server_t const&) = delete;
|
||||
server_t(server_t&&) = delete;
|
||||
|
||||
void
|
||||
start();
|
||||
|
||||
void
|
||||
stop(bool = false);
|
||||
|
||||
server_t& operator=(server_t const&) = delete;
|
||||
server_t& operator=(server_t&&) = delete;
|
||||
|
||||
protected:
|
||||
void
|
||||
on_error(request_t&, response_t&, common_error_t) override;
|
||||
|
||||
void
|
||||
on_exception(request_t&, response_t&) noexcept override;
|
||||
|
||||
// fixme: void shutdown_stream();
|
||||
|
||||
private:
|
||||
common::diagnostics_t::proxy_t
|
||||
trace();
|
||||
|
||||
void
|
||||
init_accept(common::io::acceptor_t&);
|
||||
|
||||
void
|
||||
on_accept(common::io::acceptor_t&, std::error_code const&);
|
||||
|
||||
common::diagnostics_t& diagnostics_;
|
||||
configuration_t configuration_;
|
||||
acceptor_set_t const acceptors_;
|
||||
request_handler_t handler_;
|
||||
supervisor_t supervisor_;
|
||||
|
||||
};
|
||||
|
||||
} // namespace code::seafire::server
|
||||
|
||||
#endif
|
154
code/seafire/server/session.cxx
Normal file
154
code/seafire/server/session.cxx
Normal file
@ -0,0 +1,154 @@
|
||||
#include <code/seafire/server/session.hxx>
|
||||
|
||||
#include <code/seafire/server/diagnostics.hxx>
|
||||
#include <code/seafire/server/supervisor.hxx>
|
||||
#include <code/seafire/server/transaction.hxx>
|
||||
|
||||
#include <code/seafire/common/io/error.hxx>
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
session_t::
|
||||
~session_t() noexcept
|
||||
{
|
||||
trace() << "~session_t()...";
|
||||
}
|
||||
|
||||
supervisor_t&
|
||||
session_t::
|
||||
owner()
|
||||
{
|
||||
return owner_;
|
||||
}
|
||||
|
||||
session_t::stats_t
|
||||
session_t::
|
||||
stats() const
|
||||
{
|
||||
return stats_;
|
||||
}
|
||||
|
||||
void
|
||||
session_t::
|
||||
start()
|
||||
{
|
||||
trace() << "start()...";
|
||||
init_transaction();
|
||||
}
|
||||
|
||||
void
|
||||
session_t::
|
||||
stop()
|
||||
{
|
||||
// fixme: should we really ignore the error code at this point?
|
||||
//
|
||||
|
||||
std::error_code ignored_ec;
|
||||
stream_->close(ignored_ec);
|
||||
}
|
||||
|
||||
session_t::
|
||||
session_t(common::diagnostics_t& diagnostics,
|
||||
error_handler_t& error_handler,
|
||||
supervisor_t& owner,
|
||||
std::unique_ptr<common::io::stream_t> stream,
|
||||
request_handler_t& handler)
|
||||
: diagnostics_{diagnostics},
|
||||
error_handler_{error_handler},
|
||||
owner_{owner},
|
||||
stream_{std::move(stream)},
|
||||
handler_{handler},
|
||||
connection_{*stream_, 1024*1024} // FIXME: magic number.
|
||||
{
|
||||
trace() << "session()...";
|
||||
}
|
||||
|
||||
void
|
||||
session_t::
|
||||
init_transaction()
|
||||
{
|
||||
trace() << "init_transaction()";
|
||||
|
||||
auto self = shared_from_this();
|
||||
|
||||
auto bound = [this, self](std::error_code const& ec,
|
||||
transaction_t::result_t result)
|
||||
{
|
||||
on_tx_complete(ec, result == transaction_t::result_t::complete_closed);
|
||||
};
|
||||
|
||||
// fixme: This will potentially destroy the previous transaction.
|
||||
// Should we detect this and throw? Shouldn't ever happen...
|
||||
//
|
||||
current_tx_ = make_transaction(diagnostics_,
|
||||
std::chrono::seconds{0},
|
||||
error_handler_,
|
||||
connection_,
|
||||
handler_,
|
||||
bound);
|
||||
|
||||
current_tx_->start();
|
||||
}
|
||||
|
||||
void
|
||||
session_t::
|
||||
on_tx_complete(std::error_code const& ec, bool close)
|
||||
{
|
||||
trace() << "on_tx_complete()...";
|
||||
|
||||
auto self = shared_from_this();
|
||||
|
||||
current_tx_.reset();
|
||||
|
||||
if (!ec) {
|
||||
if (close) {
|
||||
trace() << "on_tx_complete(): close requested...";
|
||||
|
||||
// Initiate a graceful close of this session/connection.
|
||||
//
|
||||
init_close();
|
||||
}
|
||||
else {
|
||||
trace() << "on_tx_complete(): keep-alive requested...";
|
||||
|
||||
// We're apparently not done, initiate a new transaction.
|
||||
//
|
||||
init_transaction();
|
||||
}
|
||||
}
|
||||
else {
|
||||
trace() << "on_tx_complete(): error: " << ec;
|
||||
|
||||
// If an error occurred, we just remove ourself from the
|
||||
// supervisor which will eventually close the connection
|
||||
// when we are destroyed.
|
||||
//
|
||||
owner().remove(self);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
session_t::
|
||||
init_close()
|
||||
{
|
||||
auto self = shared_from_this();
|
||||
|
||||
auto bound = [this, self]()
|
||||
{
|
||||
owner().remove(self);
|
||||
};
|
||||
|
||||
stream_->async_graceful_close(bound);
|
||||
}
|
||||
|
||||
common::diagnostics_t::proxy_t
|
||||
session_t::
|
||||
trace()
|
||||
{
|
||||
return diagnostics_ << session_category();
|
||||
}
|
||||
|
||||
} // namespace code::seafire::server
|
130
code/seafire/server/session.hxx
Normal file
130
code/seafire/server/session.hxx
Normal file
@ -0,0 +1,130 @@
|
||||
#ifndef code__seafire__server__session_hxx_
|
||||
#define code__seafire__server__session_hxx_
|
||||
|
||||
#include <code/seafire/common/diagnostics.hxx>
|
||||
|
||||
#include <code/seafire/common/io/stream.hxx>
|
||||
|
||||
#include <code/seafire/server/counter.hxx>
|
||||
#include <code/seafire/server/error-handler.hxx>
|
||||
#include <code/seafire/server/request-handler.hxx>
|
||||
|
||||
#include <code/seafire/protocol/connection.hxx>
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <system_error>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
class supervisor_t;
|
||||
class transaction_t;
|
||||
|
||||
class session_t
|
||||
: public std::enable_shared_from_this<session_t>
|
||||
{
|
||||
public:
|
||||
class info_t;
|
||||
|
||||
struct stats_t
|
||||
{
|
||||
/// The total number of transactions handled by this session.
|
||||
///
|
||||
counter_t transaction_counter;
|
||||
|
||||
};
|
||||
|
||||
virtual
|
||||
~session_t() noexcept;
|
||||
|
||||
supervisor_t&
|
||||
owner();
|
||||
|
||||
stats_t
|
||||
stats() const;
|
||||
|
||||
void
|
||||
start();
|
||||
|
||||
void
|
||||
stop();
|
||||
|
||||
public:
|
||||
friend
|
||||
std::shared_ptr<session_t>
|
||||
make_shared_session(common::diagnostics_t&,
|
||||
error_handler_t&,
|
||||
supervisor_t&,
|
||||
std::unique_ptr<common::io::stream_t>,
|
||||
request_handler_t&);
|
||||
|
||||
session_t(common::diagnostics_t&,
|
||||
error_handler_t&,
|
||||
supervisor_t&,
|
||||
std::unique_ptr<common::io::stream_t>,
|
||||
request_handler_t&);
|
||||
|
||||
session_t(session_t const&) = delete;
|
||||
session_t(session_t&&) = delete;
|
||||
|
||||
void
|
||||
init_transaction();
|
||||
|
||||
void
|
||||
on_tx_complete(std::error_code const&, bool);
|
||||
|
||||
void
|
||||
init_close();
|
||||
|
||||
void
|
||||
init_close_read();
|
||||
|
||||
void
|
||||
on_close_read(std::error_code const&);
|
||||
|
||||
session_t& operator=(session_t const&) = delete;
|
||||
session_t& operator=(session_t&&) = delete;
|
||||
|
||||
private:
|
||||
common::diagnostics_t::proxy_t
|
||||
trace();
|
||||
|
||||
std::mutex protector_;
|
||||
|
||||
common::diagnostics_t& diagnostics_;
|
||||
error_handler_t& error_handler_;
|
||||
supervisor_t& owner_;
|
||||
std::unique_ptr<common::io::stream_t> stream_;
|
||||
request_handler_t& handler_;
|
||||
protocol::connection_t connection_;
|
||||
stats_t stats_;
|
||||
std::shared_ptr<transaction_t> current_tx_;
|
||||
|
||||
char throwaway_[1];
|
||||
|
||||
};
|
||||
|
||||
class session_t::info_t
|
||||
{
|
||||
};
|
||||
|
||||
inline
|
||||
std::shared_ptr<session_t>
|
||||
make_shared_session(common::diagnostics_t& diagnostics,
|
||||
error_handler_t& error_handler,
|
||||
supervisor_t& supervisor,
|
||||
std::unique_ptr<common::io::stream_t> stream,
|
||||
request_handler_t& request_handler)
|
||||
{
|
||||
return std::make_shared<session_t>(diagnostics,
|
||||
error_handler,
|
||||
supervisor,
|
||||
std::move(stream),
|
||||
request_handler);
|
||||
}
|
||||
|
||||
} // namespace code::seafire::server
|
||||
|
||||
#endif
|
88
code/seafire/server/supervisor.cxx
Normal file
88
code/seafire/server/supervisor.cxx
Normal file
@ -0,0 +1,88 @@
|
||||
#include <code/seafire/server/supervisor.hxx>
|
||||
|
||||
#include <code/seafire/server/diagnostics.hxx>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
supervisor_t::
|
||||
supervisor_t(common::diagnostics_t& diagnostics,
|
||||
error_handler_t& error_handler,
|
||||
request_handler_t& handler)
|
||||
: diagnostics_{diagnostics},
|
||||
error_handler_{error_handler},
|
||||
handler_{handler}
|
||||
{}
|
||||
|
||||
void
|
||||
supervisor_t::
|
||||
async_accept(common::io::acceptor_t& acceptor, accept_handler_t handler)
|
||||
{
|
||||
trace() << "async_accept()...";
|
||||
|
||||
auto bound = [this, handler](std::error_code const& ec,
|
||||
std::unique_ptr<common::io::stream_t> stream)
|
||||
{
|
||||
handler(ec);
|
||||
|
||||
if (!ec) {
|
||||
this->start(std::move(stream));
|
||||
}
|
||||
};
|
||||
|
||||
acceptor.async_accept(bound);
|
||||
}
|
||||
|
||||
void
|
||||
supervisor_t::
|
||||
start(std::unique_ptr<common::io::stream_t> stream)
|
||||
{
|
||||
trace() << "start()...";
|
||||
|
||||
if (!stream) {
|
||||
throw std::invalid_argument{"stream"};
|
||||
}
|
||||
|
||||
auto session = make_shared_session(diagnostics_,
|
||||
error_handler_,
|
||||
*this,
|
||||
std::move(stream),
|
||||
handler_);
|
||||
|
||||
{
|
||||
std::lock_guard lock{protector_};
|
||||
sessions_.emplace(session);
|
||||
}
|
||||
|
||||
session->start();
|
||||
}
|
||||
|
||||
void
|
||||
supervisor_t::
|
||||
stop_all()
|
||||
{
|
||||
trace() << "stop_all()...";
|
||||
|
||||
std::lock_guard lock{protector_};
|
||||
|
||||
for (auto const& j : sessions_) {
|
||||
j->stop();
|
||||
}
|
||||
}
|
||||
|
||||
common::diagnostics_t::proxy_t
|
||||
supervisor_t::
|
||||
trace() const
|
||||
{
|
||||
return diagnostics_ << supervisor_category();
|
||||
}
|
||||
|
||||
void
|
||||
supervisor_t::
|
||||
remove(session_ptr_t session)
|
||||
{
|
||||
std::lock_guard lock{protector_};
|
||||
sessions_.erase(session);
|
||||
}
|
||||
|
||||
} // namespace code::seafire::server
|
92
code/seafire/server/supervisor.hxx
Normal file
92
code/seafire/server/supervisor.hxx
Normal file
@ -0,0 +1,92 @@
|
||||
#ifndef code__seafire__server__supervisor_hxx_
|
||||
#define code__seafire__server__supervisor_hxx_
|
||||
|
||||
#include <code/seafire/common/diagnostics.hxx>
|
||||
|
||||
#include <code/seafire/common/io/acceptor.hxx>
|
||||
#include <code/seafire/common/io/stream.hxx>
|
||||
|
||||
#include <code/seafire/server/counter.hxx>
|
||||
#include <code/seafire/server/error-handler.hxx>
|
||||
#include <code/seafire/server/request-handler.hxx>
|
||||
#include <code/seafire/server/session.hxx>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
/// Implements a session supervisor.
|
||||
///
|
||||
class supervisor_t
|
||||
{
|
||||
friend session_t;
|
||||
|
||||
using session_ptr_t = std::shared_ptr<session_t>;
|
||||
|
||||
public:
|
||||
class info_t;
|
||||
|
||||
using accept_handler_t = std::function<void(std::error_code)>;
|
||||
|
||||
/// Tracks supervisor statistics counters.
|
||||
///
|
||||
struct counters_t
|
||||
{
|
||||
/// Tracks the total number of sessions.
|
||||
///
|
||||
counter_t total_session_count;
|
||||
|
||||
/// Tracks the total number of currently active sessions.
|
||||
///
|
||||
counter_t active_session_count;
|
||||
|
||||
};
|
||||
|
||||
supervisor_t(common::diagnostics_t&,
|
||||
error_handler_t&,
|
||||
request_handler_t&);
|
||||
|
||||
supervisor_t(supervisor_t const&) = delete;
|
||||
supervisor_t(supervisor_t&&) = delete;
|
||||
|
||||
void
|
||||
async_accept(common::io::acceptor_t&, accept_handler_t);
|
||||
|
||||
void
|
||||
start(std::unique_ptr<common::io::stream_t>);
|
||||
|
||||
void
|
||||
stop_all();
|
||||
|
||||
supervisor_t& operator=(supervisor_t const&) = delete;
|
||||
supervisor_t& operator=(supervisor_t&&) = delete;
|
||||
|
||||
private:
|
||||
common::diagnostics_t::proxy_t
|
||||
trace() const;
|
||||
|
||||
void
|
||||
remove(session_ptr_t);
|
||||
|
||||
std::mutex protector_;
|
||||
|
||||
common::diagnostics_t& diagnostics_;
|
||||
error_handler_t& error_handler_;
|
||||
request_handler_t& handler_;
|
||||
std::set<session_ptr_t> sessions_;
|
||||
counters_t stats_;
|
||||
|
||||
};
|
||||
|
||||
class supervisor_t::info_t
|
||||
{
|
||||
public:
|
||||
};
|
||||
|
||||
} // namespace code::seafire::server
|
||||
|
||||
#endif
|
435
code/seafire/server/transaction.cxx
Normal file
435
code/seafire/server/transaction.cxx
Normal file
@ -0,0 +1,435 @@
|
||||
#include "code/seafire/protocol/status-code.hxx"
|
||||
#include <code/seafire/server/transaction.hxx>
|
||||
|
||||
#include <code/seafire/server/diagnostics.hxx>
|
||||
#include <code/seafire/server/version.hxx>
|
||||
|
||||
#include <code/seafire/protocol/rfc7230/connection.hxx>
|
||||
#include <code/seafire/protocol/rfc7230/content-length.hxx>
|
||||
#include <code/seafire/protocol/rfc7231/content-type.hxx>
|
||||
#include <code/seafire/protocol/rfc7231/date.hxx>
|
||||
#include <code/seafire/protocol/rfc7231/server.hxx>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
// fixme: determine which member functions need to lock the mutex.
|
||||
//
|
||||
|
||||
transaction_t::
|
||||
~transaction_t() noexcept(false)
|
||||
{
|
||||
trace() << "~transaction_t()...";
|
||||
}
|
||||
|
||||
error_handler_t&
|
||||
transaction_t::
|
||||
get_error_handler()
|
||||
{
|
||||
return error_handler_;
|
||||
}
|
||||
|
||||
protocol::connection_t&
|
||||
transaction_t::
|
||||
connection()
|
||||
{
|
||||
return connection_;
|
||||
}
|
||||
|
||||
asio::any_io_executor const&
|
||||
transaction_t::
|
||||
get_executor()
|
||||
{
|
||||
return connection().get_executor();
|
||||
}
|
||||
|
||||
void
|
||||
transaction_t::
|
||||
start()
|
||||
{
|
||||
init_read();
|
||||
}
|
||||
|
||||
void
|
||||
transaction_t::
|
||||
cancel()
|
||||
{
|
||||
// fixme: connection().cancel();
|
||||
}
|
||||
|
||||
common::allocator_t&
|
||||
transaction_t::
|
||||
memory()
|
||||
{
|
||||
return allocator_;
|
||||
}
|
||||
|
||||
protocol::request_t const&
|
||||
transaction_t::
|
||||
get_request() const
|
||||
{
|
||||
return request_;
|
||||
}
|
||||
|
||||
std::istream&
|
||||
transaction_t::
|
||||
get_request_content()
|
||||
{
|
||||
return request_content_stream_;
|
||||
}
|
||||
|
||||
common::extension_context_t&
|
||||
transaction_t::
|
||||
get_request_extensions()
|
||||
{
|
||||
return request_extensions_;
|
||||
}
|
||||
|
||||
void
|
||||
transaction_t::
|
||||
register_finalizer(finalizer_t* f)
|
||||
{
|
||||
if (f == nullptr) {
|
||||
throw std::invalid_argument{"invalid finalizer"};
|
||||
}
|
||||
|
||||
std::lock_guard lock{protector_};
|
||||
finalizers_.emplace_back(f);
|
||||
}
|
||||
|
||||
void
|
||||
transaction_t::
|
||||
deregister_finalizer(finalizer_t* f)
|
||||
{
|
||||
if (f == nullptr) {
|
||||
throw std::invalid_argument{"invalid finalizer"};
|
||||
}
|
||||
|
||||
std::lock_guard lock{protector_};
|
||||
std::erase(finalizers_, f);
|
||||
}
|
||||
|
||||
protocol::response_t&
|
||||
transaction_t::
|
||||
get_response()
|
||||
{
|
||||
return response_;
|
||||
}
|
||||
|
||||
protocol::response_t const&
|
||||
transaction_t::
|
||||
get_response() const
|
||||
{
|
||||
return response_;
|
||||
}
|
||||
|
||||
common::extension_context_t&
|
||||
transaction_t::
|
||||
get_response_extensions()
|
||||
{
|
||||
return response_extensions_;
|
||||
}
|
||||
|
||||
void
|
||||
transaction_t::
|
||||
do_send_response(protocol::status_code_t s,
|
||||
common::io::const_buffers_t const& content)
|
||||
{
|
||||
trace() << "do_send_response()...";
|
||||
|
||||
std::lock_guard lock{protector_};
|
||||
|
||||
auto self = shared_from_this();
|
||||
|
||||
invoke_finalizers();
|
||||
finalize_response(s, asio::buffer_size(content));
|
||||
init_write(content);
|
||||
}
|
||||
|
||||
void
|
||||
transaction_t::
|
||||
do_send_error(common_error_t error)
|
||||
{
|
||||
auto self = shared_from_this();
|
||||
|
||||
auto bound = [this, self, error]
|
||||
{
|
||||
error_handler_.on_error(*this, *this, error);
|
||||
};
|
||||
|
||||
asio::post(get_executor(), bound);
|
||||
}
|
||||
|
||||
transaction_t::
|
||||
transaction_t(common::diagnostics_t& diagnostics,
|
||||
std::chrono::seconds request_timeout,
|
||||
error_handler_t& error_handler,
|
||||
protocol::connection_t& connection,
|
||||
request_handler_t& handler,
|
||||
completion_handler_t on_completion)
|
||||
: diagnostics_{diagnostics},
|
||||
request_timeout_{request_timeout},
|
||||
error_handler_{error_handler},
|
||||
connection_{connection},
|
||||
handler_{handler},
|
||||
on_completion_{on_completion},
|
||||
request_timeout_timer_{connection_.get_executor()},
|
||||
request_content_{10}, // fixme: make configurable buffer max,
|
||||
//request_content_{1024 * 1024 * 32}, // fixme: make configurable buffer max,
|
||||
request_content_stream_{&request_content_}
|
||||
{
|
||||
trace() << "transaction_t()...";
|
||||
}
|
||||
|
||||
common::diagnostics_t::proxy_t
|
||||
transaction_t::
|
||||
trace()
|
||||
{
|
||||
return diagnostics_ << transaction_category();
|
||||
}
|
||||
|
||||
bool
|
||||
transaction_t::
|
||||
keep_alive()
|
||||
{
|
||||
auto c = get<protocol::rfc7230::connection_t>(get_request());
|
||||
|
||||
if (c && c->close()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (get_request().version() == protocol::http_1_0 && c) {
|
||||
return c->keep_alive();
|
||||
}
|
||||
|
||||
if (get_request().version() == protocol::http_1_1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
transaction_t::
|
||||
init_read()
|
||||
{
|
||||
trace() << "init_read()...";
|
||||
|
||||
auto self = shared_from_this();
|
||||
prepare_response();
|
||||
|
||||
if (request_timeout_ > std::chrono::seconds{0}) {
|
||||
auto on_timeout = [this, self](std::error_code const& ec)
|
||||
{
|
||||
on_read_timeout(ec);
|
||||
};
|
||||
|
||||
request_timeout_timer_.expires_after(request_timeout_);
|
||||
request_timeout_timer_.async_wait(on_timeout);
|
||||
}
|
||||
|
||||
auto on_read = [this, self](std::error_code const& ec)
|
||||
{
|
||||
this->on_read(ec);
|
||||
};
|
||||
|
||||
connection().async_read(request_, request_content_, on_read);
|
||||
}
|
||||
|
||||
void
|
||||
transaction_t::
|
||||
on_read_timeout(std::error_code const& ec)
|
||||
{
|
||||
auto self = shared_from_this();
|
||||
|
||||
std::lock_guard lock{protector_};
|
||||
|
||||
if (!ec) {
|
||||
// fixme: add diagnostics...
|
||||
//
|
||||
connection_.cancel();
|
||||
send(common_error_t::request_timeout);
|
||||
}
|
||||
else if (ec != asio::error::operation_aborted) {
|
||||
// error occurred, restart timer.
|
||||
//
|
||||
request_timeout_timer_.expires_after(request_timeout_);
|
||||
|
||||
auto on_timeout = [this, self](std::error_code const& ec)
|
||||
{
|
||||
on_read_timeout(ec);
|
||||
};
|
||||
|
||||
request_timeout_timer_.async_wait(on_timeout);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
transaction_t::
|
||||
on_read(std::error_code const& ec)
|
||||
{
|
||||
// Attempt to cancel the request timeout handler, if active.
|
||||
//
|
||||
if (request_timeout_ > std::chrono::seconds{0}) {
|
||||
if (request_timeout_timer_.cancel() < 1) {
|
||||
// Timeout already happened.
|
||||
//
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// fixme: add tracing.
|
||||
//
|
||||
|
||||
if (ec) {
|
||||
// fixme: Send error response based on ec.
|
||||
//
|
||||
send(common_error_t::internal_server_error);
|
||||
return;
|
||||
}
|
||||
|
||||
// handle Expect: 100-continue
|
||||
|
||||
if (auto expect = request_.headers().get_one("expect"); expect) {
|
||||
if (request_.version() == protocol::http_1_1 && *expect == "100-continue") {
|
||||
static std::string const response{
|
||||
"HTTP/1.1 100 Continue\r\n\r\n"
|
||||
};
|
||||
|
||||
connection().get_stream().write(common::io::buffer(response));
|
||||
}
|
||||
}
|
||||
|
||||
init_dispatch();
|
||||
}
|
||||
|
||||
void
|
||||
transaction_t::
|
||||
prepare_response()
|
||||
{
|
||||
using protocol::rfc7231::date_t;
|
||||
using protocol::rfc7231::product_t;
|
||||
using protocol::rfc7231::products_t;
|
||||
using protocol::rfc7231::server_t;
|
||||
|
||||
using protocol::set;
|
||||
|
||||
set<server_t>(get_response(), products_t{
|
||||
product_t{"Seafire", LIBCODE_SEAFIRE_SERVER_VERSION_STR}
|
||||
});
|
||||
set<date_t>(get_response(), std::chrono::system_clock::now());
|
||||
|
||||
// we always respond with HTTP/1.1 since that is the highest version we support.
|
||||
//
|
||||
get_response().set_version(protocol::http_1_1);
|
||||
}
|
||||
|
||||
void
|
||||
transaction_t::
|
||||
init_dispatch()
|
||||
{
|
||||
trace() << "init_dispatch()...";
|
||||
|
||||
try {
|
||||
handler_.invoke(*this, *this);
|
||||
}
|
||||
catch (...) {
|
||||
trace() << "handler threw exception, dispatching to exception handler...";
|
||||
|
||||
try {
|
||||
get_error_handler().on_exception(*this, *this);
|
||||
}
|
||||
catch (...) {
|
||||
trace() << "exception handler threw, we're out of luck...";
|
||||
|
||||
// Fuck, we're out of luck.
|
||||
//
|
||||
send(common_error_t::internal_server_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
transaction_t::
|
||||
invoke_finalizers()
|
||||
{
|
||||
for (auto const& j : finalizers_) {
|
||||
invoke_finalizer(j, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
transaction_t::
|
||||
finalize_response(protocol::status_code_t const& s,
|
||||
std::size_t content_length)
|
||||
{
|
||||
get_response().set_status(s);
|
||||
|
||||
namespace rfc7230 = protocol::rfc7230;
|
||||
namespace rfc7231 = protocol::rfc7231;
|
||||
|
||||
if (get_request().version() == protocol::http_1_0) {
|
||||
if (keep_alive()) {
|
||||
set<rfc7230::connection_t>(get_response(), "keep-alive");
|
||||
}
|
||||
else {
|
||||
set<rfc7230::connection_t>(get_response(), "close");
|
||||
}
|
||||
}
|
||||
else if (get_request().version() == protocol::http_1_1) {
|
||||
if (keep_alive()) {
|
||||
erase<rfc7230::connection_t>(get_response());
|
||||
}
|
||||
else {
|
||||
set<rfc7230::connection_t>(get_response(), "close");
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure content-type is always set.
|
||||
//
|
||||
if (!has<rfc7231::content_type_t>(get_response())) {
|
||||
set<rfc7231::content_type_t>(get_response(), protocol::media_type_t{"application", "octet-stream"});
|
||||
}
|
||||
|
||||
// Always set content length to the actual content length.
|
||||
//
|
||||
set<rfc7230::content_length_t>(get_response(), content_length);
|
||||
}
|
||||
|
||||
void
|
||||
transaction_t::
|
||||
init_write(common::io::const_buffers_t const& content)
|
||||
{
|
||||
trace() << "init write...()";
|
||||
|
||||
auto self = shared_from_this();
|
||||
std::lock_guard lock{protector_};
|
||||
|
||||
auto bound = [this, self](std::error_code const& ec)
|
||||
{
|
||||
on_write(ec);
|
||||
};
|
||||
|
||||
connection().async_write(get_response(), content, bound);
|
||||
}
|
||||
|
||||
void
|
||||
transaction_t::
|
||||
on_write(std::error_code const& ec)
|
||||
{
|
||||
trace() << "on_write()...";
|
||||
|
||||
auto self = shared_from_this();
|
||||
std::lock_guard lock{protector_};
|
||||
|
||||
auto bound = [self, ec, close = !keep_alive(), cb = on_completion_]()
|
||||
{
|
||||
cb(ec, close ? complete_closed : complete);
|
||||
};
|
||||
|
||||
asio::post(get_executor(), bound);
|
||||
}
|
||||
|
||||
} // namespace code::seafire::server
|
216
code/seafire/server/transaction.hxx
Normal file
216
code/seafire/server/transaction.hxx
Normal file
@ -0,0 +1,216 @@
|
||||
#ifndef code__seafire__server__transaction_hxx_
|
||||
#define code__seafire__server__transaction_hxx_
|
||||
|
||||
#include <code/seafire/common/allocator.hxx>
|
||||
#include <code/seafire/common/diagnostics.hxx>
|
||||
#include <code/seafire/common/extension-context.hxx>
|
||||
|
||||
#include <code/seafire/common/io/buffer.hxx>
|
||||
|
||||
#include <code/seafire/server/error-handler.hxx>
|
||||
#include <code/seafire/server/request-handler.hxx>
|
||||
#include <code/seafire/server/request.hxx>
|
||||
#include <code/seafire/server/response.hxx>
|
||||
|
||||
#include <code/seafire/protocol/connection.hxx>
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace code::seafire::server
|
||||
{
|
||||
|
||||
class transaction_t
|
||||
: public std::enable_shared_from_this<transaction_t>,
|
||||
request_t,
|
||||
response_t
|
||||
{
|
||||
public:
|
||||
struct configuration_t;
|
||||
|
||||
/// The result of this transaction.
|
||||
///
|
||||
enum result_t
|
||||
{
|
||||
/// Indicates a completed transation.
|
||||
///
|
||||
complete,
|
||||
|
||||
/// Indicates a completed transation and that the
|
||||
/// underlying connection should be closed.
|
||||
complete_closed
|
||||
|
||||
};
|
||||
|
||||
/// Completion handler type.
|
||||
///
|
||||
using completion_handler_t = std::function<void(std::error_code, result_t)>;
|
||||
|
||||
virtual
|
||||
~transaction_t() noexcept(false);
|
||||
|
||||
error_handler_t&
|
||||
get_error_handler();
|
||||
|
||||
protocol::connection_t&
|
||||
connection();
|
||||
|
||||
asio::any_io_executor const&
|
||||
get_executor();
|
||||
|
||||
void
|
||||
start();
|
||||
|
||||
void
|
||||
cancel();
|
||||
|
||||
// =================
|
||||
// Common interface.
|
||||
//
|
||||
|
||||
common::allocator_t&
|
||||
memory() override;
|
||||
|
||||
// ==================
|
||||
// Request interface.
|
||||
//
|
||||
// fixme: make these private.
|
||||
//
|
||||
|
||||
protocol::request_t const&
|
||||
get_request() const override;
|
||||
|
||||
std::istream&
|
||||
get_request_content() override;
|
||||
|
||||
common::extension_context_t&
|
||||
get_request_extensions() override;
|
||||
|
||||
// ===================
|
||||
// Response interface.
|
||||
//
|
||||
// fixme: make these private.
|
||||
//
|
||||
|
||||
void
|
||||
register_finalizer(finalizer_t* f) override;
|
||||
|
||||
void
|
||||
deregister_finalizer(finalizer_t* f) override;
|
||||
|
||||
protocol::response_t&
|
||||
get_response() override;
|
||||
|
||||
protocol::response_t const&
|
||||
get_response() const override;
|
||||
|
||||
common::extension_context_t&
|
||||
get_response_extensions() override;
|
||||
|
||||
void
|
||||
do_send_response(protocol::status_code_t,
|
||||
common::io::const_buffers_t const&) override;
|
||||
|
||||
void
|
||||
do_send_error(common_error_t error) override;
|
||||
|
||||
protected:
|
||||
template<typename... Args>
|
||||
friend
|
||||
std::shared_ptr<transaction_t>
|
||||
make_transaction(Args&&...);
|
||||
|
||||
protected:
|
||||
/// fixme: replace request_timeout with a configuration_t object.
|
||||
///
|
||||
transaction_t(common::diagnostics_t&,
|
||||
std::chrono::seconds request_timeout,
|
||||
error_handler_t&,
|
||||
protocol::connection_t&,
|
||||
request_handler_t&,
|
||||
completion_handler_t);
|
||||
|
||||
private:
|
||||
common::diagnostics_t::proxy_t
|
||||
trace();
|
||||
|
||||
bool
|
||||
keep_alive();
|
||||
|
||||
void
|
||||
init_read();
|
||||
|
||||
void
|
||||
on_read_timeout(std::error_code const&);
|
||||
|
||||
void
|
||||
on_read(std::error_code const&);
|
||||
|
||||
void
|
||||
prepare_response();
|
||||
|
||||
void
|
||||
init_dispatch();
|
||||
|
||||
void
|
||||
invoke_finalizers();
|
||||
|
||||
void
|
||||
finalize_response(protocol::status_code_t const&,
|
||||
std::size_t);
|
||||
|
||||
void
|
||||
init_write(common::io::const_buffers_t const&);
|
||||
|
||||
void
|
||||
on_write(std::error_code const&);
|
||||
|
||||
std::recursive_mutex protector_;
|
||||
|
||||
common::diagnostics_t& diagnostics_;
|
||||
std::chrono::seconds request_timeout_;
|
||||
error_handler_t& error_handler_;
|
||||
common::allocator_t allocator_;
|
||||
common::extension_context_t request_extensions_;
|
||||
common::extension_context_t response_extensions_;
|
||||
protocol::connection_t& connection_;
|
||||
request_handler_t& handler_;
|
||||
completion_handler_t on_completion_;
|
||||
asio::steady_timer request_timeout_timer_;
|
||||
protocol::request_t request_;
|
||||
asio::streambuf request_content_;
|
||||
std::istream request_content_stream_;
|
||||
protocol::response_t response_;
|
||||
std::vector<finalizer_t*> finalizers_;
|
||||
|
||||
};
|
||||
|
||||
/// Holds transaction configuration parameters.
|
||||
///
|
||||
struct transaction_t::configuration_t
|
||||
{
|
||||
/// Holds the request timeout (in seconds).
|
||||
///
|
||||
/// Request timeout is completely disabled if this is 0.
|
||||
///
|
||||
std::chrono::seconds request_timeout;
|
||||
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
std::shared_ptr<transaction_t>
|
||||
make_transaction(Args&&... args)
|
||||
{
|
||||
return std::shared_ptr<transaction_t>{
|
||||
new transaction_t{
|
||||
std::forward<Args>(args)...
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace code::seafire::server
|
||||
|
||||
#endif
|
37
code/seafire/server/version.hxx.in
Normal file
37
code/seafire/server/version.hxx.in
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef code__seafire__server__version_hxx_
|
||||
#define code__seafire__server__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 LIBCODE_SEAFIRE_SERVER_VERSION $libcode_seafire_server.version.project_number$ULL
|
||||
#define LIBCODE_SEAFIRE_SERVER_VERSION_STR "$libcode_seafire_server.version.project$"
|
||||
#define LIBCODE_SEAFIRE_SERVER_VERSION_ID "$libcode_seafire_server.version.project_id$"
|
||||
#define LIBCODE_SEAFIRE_SERVER_VERSION_FULL "$libcode_seafire_server.version$"
|
||||
|
||||
#define LIBCODE_SEAFIRE_SERVER_VERSION_MAJOR $libcode_seafire_server.version.major$
|
||||
#define LIBCODE_SEAFIRE_SERVER_VERSION_MINOR $libcode_seafire_server.version.minor$
|
||||
#define LIBCODE_SEAFIRE_SERVER_VERSION_PATCH $libcode_seafire_server.version.patch$
|
||||
|
||||
#define LIBCODE_SEAFIRE_SERVER_PRE_RELEASE $libcode_seafire_server.version.pre_release$
|
||||
|
||||
#define LIBCODE_SEAFIRE_SERVER_SNAPSHOT_SN $libcode_seafire_server.version.snapshot_sn$ULL
|
||||
#define LIBCODE_SEAFIRE_SERVER_SNAPSHOT_ID "$libcode_seafire_server.version.snapshot_id$"
|
||||
|
||||
#endif
|
15
manifest
Normal file
15
manifest
Normal file
@ -0,0 +1,15 @@
|
||||
: 1
|
||||
name: libcode-seafire-server
|
||||
version: 0.1.0-a.0.z
|
||||
language: c++
|
||||
summary: libcode-seafire-server C++ library
|
||||
license: BSD-4-Clause
|
||||
description-file: README.md
|
||||
url: https://helloryan.se/code/
|
||||
email: ryan@helloryan.se
|
||||
depends: * build2 >= 0.17.0
|
||||
depends: * bpkg >= 0.17.0
|
||||
depends: libasio ^1.29.0
|
||||
depends: libcode-uri ^0.1.0-
|
||||
depends: libcode-seafire-common ^0.1.0-
|
||||
depends: libcode-seafire-protocol ^0.1.0-
|
19
repositories.manifest
Normal file
19
repositories.manifest
Normal file
@ -0,0 +1,19 @@
|
||||
: 1
|
||||
summary: libcode-seafire-server 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/code/libcode-uri.git##HEAD
|
||||
|
||||
:
|
||||
role: prerequisite
|
||||
location: https://code.helloryan.se/code/libcode-seafire-common.git##HEAD
|
||||
|
||||
:
|
||||
role: prerequisite
|
||||
location: https://code.helloryan.se/code/libcode-seafire-protocol.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/}
|
Loading…
x
Reference in New Issue
Block a user