Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for an plain HTTP backend #63

Merged
merged 4 commits into from
Jan 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions .github/workflows/upload.yml
Original file line number Diff line number Diff line change
Expand Up @@ -351,20 +351,24 @@ jobs:
docker tag ${{ env.frontend-image }} $NAME-frontend:latest
- run: docker build . --file Dockerfile.alt --tag test-image --tag ${{ env.um-image }} --tag $DOCKER_REGISTRY/$NAME:$BUILD_VERSION
- run: docker run --name test -p 8443:8443 --rm -d test-image
- name: test
- name: test HTTPS
run: |
curl -s -k --key backend/certs/key.pem --cert backend/certs/server.pem -o output.log --url https://localhost:8443/index.html
cat output.log | grep -q "<title>User Management</title>"
- run: docker kill test
- run: docker run --name test -p 8080:8080 --rm -d test-image dist -a "0.0.0.0" -p 8080 -n 4
- name: test HTTP
run: |
curl -s -o output.log --url http://localhost:8080/index.html
cat output.log | grep -q "<title>User Management</title>"
- run: docker kill test
- run: docker push ${{ env.um-image }}
- #if: github.event_name == 'push'
uses: docker/login-action@v1
- uses: docker/login-action@v1
with:
registry: princechrismc.jfrog.io
username: ${{ github.repository_owner }}
password: ${{ secrets.JFROG_RTFACT_PASSWORD }}
- name: upload
#if: github.event_name == 'push'
run: docker push $DOCKER_REGISTRY/$NAME:$BUILD_VERSION

upload:
Expand Down
3 changes: 2 additions & 1 deletion backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ ADD certs /opt/um/certs

WORKDIR /opt/um

ENTRYPOINT user_database_app dist certs -a "0.0.0.0" -p 8443 -n 4
ENTRYPOINT ["user_database_app"]
CMD ["dist", "certs", "-a", "0.0.0.0", "-p", "8443", "-n", "4"]
32 changes: 27 additions & 5 deletions backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ conan config set general.revisions_enabled=1

*Note*: You will need to clear your conan cache, use `conan remove -f '*'`

<!--> TODO: Add instructions for installing musl libc settings <-->

### Conan Lockfile

To create the top level `conan.lock` run:
Expand All @@ -34,28 +36,48 @@ conan lock create conanfile.py --lockfile=conan.lock --lockfile-out=build/conan.
cd build && conan install .. --lockfile=conan.lock
```

## Development

### Update Conan Lockfile

```sh
conan lock create conanfile.py --version=1.0.0-dev.0 --base --update
```

```sh
cd build && conan install .. -s build_type=Debug --lockfile=../conan.lock
```

## Usage

### Package
### Package Back-end

```sh
conan create . 1.0.0-dev.0+`git rev-parse --short HEAD`@
```

### Deploy
### Install Application

> :notebook: This step requires the [packing](#package) to be completed first

```sh
conan install user-managment/1.0.0-dev.0+`git rev-parse --short HEAD`
```

## Development
### Build Docker Image

```sh
conan lock create conanfile.py --version=1.0.0-dev.0 --base --update
docker build . -f Dockerfile -t user-managment-backend:1.0.0-dev.0 # Docker does not support SemVer build information
```

## Run Container

```sh
docker run --rm -d -p 8443:8443 -v "$(pwd):/dist" user-managment-backend:1.0.0-dev.0
```

> :notebook: By default the back-end image is setup for HTTPS for unsecure transport use the following

```sh
conan install .. -s build_type=Debug --lockfile=../conan.lock
docker run --rm -d -p 8080:8080 -v "$(pwd):/dist" user-managment-backend:1.0.0-dev.0 dist -a "0.0.0.0" -p 8080 -n 4
```
4 changes: 2 additions & 2 deletions backend/conan.lock
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"context": "host"
},
"9": {
"ref": "openssl/1.1.1i#55d4cf80244573060fbd973268276e3e",
"ref": "openssl/1.1.1i#d8a9ac920ca866a9f6e09e0010110892",
"context": "host"
},
"10": {
Expand All @@ -85,7 +85,7 @@
"context": "host"
},
"14": {
"ref": "catch2/2.13.4#5693be36c9806ce8c4a7b57aa66f242a",
"ref": "catch2/2.13.4#d28e651c93804db90fc79835b058ee96",
"context": "host"
}
},
Expand Down
61 changes: 40 additions & 21 deletions backend/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,32 +67,51 @@ auto server_handler(const std::string &root_dir, user_database &db) {
return router;
}

template <bool enable_tls>
struct server_traits : restinio::traits_t<restinio::asio_timer_manager_t, server_logger, handler::router> {};
template <>
struct server_traits<true> : restinio::tls_traits_t<restinio::asio_timer_manager_t, server_logger, handler::router> {};

template <typename traits>
restinio::run_on_thread_pool_settings_t<traits> make_settings(const app_args_t &args) {
return restinio::on_thread_pool<traits>(args.pool_size)
.address(args.address)
.port(args.port)
.read_next_http_message_timelimit(10s)
.write_http_response_timelimit(1s)
.handle_request_timeout(1s);
}

namespace ssl = restinio::asio_ns::ssl;
int main(int argc, char const *argv[]) {
const auto args = app_args_t::parse(argc, argv);
if(!args.has_value())
return static_cast<int>(args.error());
if (!args.has_value()) return static_cast<int>(args.error());

// Setup the empty database
user_database db;

const auto enable_tls = args->certs_dir.has_value();

try {
ssl::context tls_context{ssl::context::tls};
tls_context.set_options(ssl::context::default_workarounds | ssl::context::no_sslv2 |
ssl::context::no_sslv3 | ssl::context::no_tlsv1 |
ssl::context::single_dh_use);

tls_context.use_certificate_chain_file(args->certs_dir + "/server.pem");
tls_context.use_private_key_file(args->certs_dir + "/key.pem", ssl::context::pem);
tls_context.use_tmp_dh_file(args->certs_dir + "/dh2048.pem");

user_database db;
using traits_t = restinio::tls_traits_t<restinio::asio_timer_manager_t, server_logger, handler::router>;
restinio::run(restinio::on_thread_pool<traits_t>(args->pool_size)
.address(args->address)
.port(args->port)
.request_handler(server_handler(args->root_dir, db))
.read_next_http_message_timelimit(10s)
.write_http_response_timelimit(1s)
.handle_request_timeout(1s)
.tls_context(std::move(tls_context)));
if (enable_tls) {
ssl::context tls_context{ssl::context::tls};
tls_context.set_options(ssl::context::default_workarounds | ssl::context::no_sslv2 | ssl::context::no_sslv3 |
ssl::context::no_tlsv1 | ssl::context::no_tlsv1_1 | ssl::context::single_dh_use);

const auto &certs_dir = args->certs_dir.value();
tls_context.use_certificate_chain_file(certs_dir + "/server.pem");
tls_context.use_private_key_file(certs_dir + "/key.pem", ssl::context::pem);
tls_context.use_tmp_dh_file(certs_dir + "/dh2048.pem");

auto pool_settings = make_settings<server_traits<true>>(args.value());
pool_settings.tls_context(std::move(tls_context)).request_handler(server_handler(args->root_dir, db));
restinio::run(std::move(pool_settings));
} else {
auto pool_settings = make_settings<server_traits<false>>(args.value());
pool_settings.request_handler(server_handler(args->root_dir, db));
restinio::run(std::move(pool_settings));
}

} catch (const std::exception &ex) {
std::cerr << "Error: " << ex.what() << std::endl;
return 1;
Expand Down
3 changes: 2 additions & 1 deletion backend/src/utility/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
find_package(lyra 1.5.0 REQUIRED)
find_package(fmt 7.1.2 REQUIRED)
find_package(expected-lite 0.5.0 REQUIRED)
find_package(optional-lite 3.4.0 REQUIRED)
find_package(spdlog 1.8.1 REQUIRED)

add_library(app_args "app_args.hpp" "app_args.cpp")
target_include_directories(app_args PUBLIC "..")
set_target_properties(app_args PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(app_args PUBLIC nonstd::expected-lite PRIVATE bfg::lyra fmt::fmt)
target_link_libraries(app_args PUBLIC nonstd::expected-lite nonstd::optional-lite PRIVATE bfg::lyra fmt::fmt)

add_library(server_logger "server_logger.hpp")
target_include_directories(server_logger PUBLIC "..")
Expand Down
5 changes: 4 additions & 1 deletion backend/src/utility/app_args.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
#include <fmt/printf.h>

#include <iostream>

#define LYRA_CONFIG_OPTIONAL_TYPE nonstd::optional

#include <lyra/lyra.hpp>

nonstd::expected<app_args_t, app_args_t::result> app_args_t::parse(
Expand All @@ -21,7 +24,7 @@ nonstd::expected<app_args_t, app_args_t::result> app_args_t::parse(
lyra::opt(args.pool_size, "thread-pool size")["-n"]["--thread-pool-size"](
fmt::format("The size of a thread pool to run server (default: {})", args.pool_size)) |
lyra::arg(args.root_dir, "root-dir")("root directory for the server to obtain requested files").required() |
lyra::arg(args.certs_dir, "certs-dir")("directory containing certificate(s) and key(s) to host the HTTP endpoint").required();
lyra::arg(args.certs_dir, "certs-dir")("directory containing certificate(s) and key(s) to host the HTTP endpoint");

const auto parse_args = cli.parse({argc, argv});
if (!parse_args) {
Expand Down
3 changes: 2 additions & 1 deletion backend/src/utility/app_args.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// MIT License

#include <nonstd/expected.hpp>
#include <nonstd/optional.hpp>

#include <string>

Expand All @@ -9,7 +10,7 @@ struct app_args_t {
std::uint16_t port{8080};
std::size_t pool_size{1};
std::string root_dir;
std::string certs_dir;
nonstd::optional<std::string> certs_dir;

enum class result { help, error };
static nonstd::expected<app_args_t, result> parse(int argc, const char *argv[]);
Expand Down