diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index cc0922e..851c290 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -10,6 +10,7 @@ set(EXAMPLES client task cppcon-2024 + milano ) foreach(EXAMPLE ${EXAMPLES}) diff --git a/examples/data/index-milano.html b/examples/data/index-milano.html new file mode 100644 index 0000000..aea54f3 --- /dev/null +++ b/examples/data/index-milano.html @@ -0,0 +1,10 @@ + + + Ciao Milano! + + + +

Ciao Milano!

+ + + diff --git a/examples/data/itcpp.png b/examples/data/itcpp.png new file mode 100644 index 0000000..12c1056 Binary files /dev/null and b/examples/data/itcpp.png differ diff --git a/examples/milano.cpp b/examples/milano.cpp new file mode 100644 index 0000000..6800480 --- /dev/null +++ b/examples/milano.cpp @@ -0,0 +1,97 @@ +// examples/http-server.cpp -*-C++-*- +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include "demo_algorithm.hpp" +#include "demo_error.hpp" +#include "demo_scope.hpp" +#include "demo_task.hpp" +#include +#include +#include +#include +#include +#include + +namespace ex = beman::execution26; +namespace net = beman::net29; +using namespace std::chrono_literals; + +// ---------------------------------------------------------------------------- + +std::unordered_map files{ + {"/", "examples/data/index-milano.html"}, + {"/favicon.ico", "examples/data/favicon.ico"}, + {"/logo.png", "examples/data/logo.png"}, + {"/itcpp.png", "examples/data/itcpp.png"}, +}; + +auto timeout(auto scheduler, auto dur, ex::sender auto sender) { + return demo::when_any( + net::resume_after(scheduler, dur) + | demo::into_error([]{ return std::error_code(); }), + std::move(sender) + ); +} + +using on_exit = std::unique_ptr; +demo::task<> run_client(auto client, auto s) { + on_exit exit("client exiting"); + char buffer[8194]; + std::ostringstream out; + try { + while (true) { + auto n = co_await timeout(s, 1s, net::async_receive(client, net::buffer(buffer))); + if (n == 0u) + co_return; + // std::cout << "received=" << std::string_view(buffer, n) << "\n"; + std::istringstream in(std::string(buffer, n)); + std::string method, url, version; + if (!(in >> method >> url >> version)) + co_return; + auto it = files.find(url); + std::cout << "url=" << url << " found=" << (it == files.end()? "no": "yes") << "\n"; + std::string content; + if (it != files.end()) { + std::ifstream fin(it->second); + out.str({}); + out << fin.rdbuf(); + content = out.str(); + } + out.str(std::string()); + out << "HTTP/1.1 200 found\r\n" + << "Content-Length: " << content.size() << "\r\n" + << "\r\n" + << content; + + content = out.str(); + co_await net::async_send(client, net::buffer(content)); + //break; + } + } catch (std::exception const& ex) { + std::cout << "received timeout! ex=" << ex.what() << "\n"; + } catch (...) { + std::cout << "received timeout!\n"; + } +} + +auto main() -> int +{ + demo::scope scope; + net::io_context context; + + scope.spawn([](auto& context, auto& scope)->demo::task<> { + net::ip::tcp::endpoint ep(net::ip::address_v4::any(), 12345); + net::ip::tcp::acceptor acceptor(context, ep); + while (true) { + auto[client, address] = co_await net::async_accept(acceptor); + std::cout << "received a connection from " << address << "\n"; + scope.spawn(run_client(std::move(client), context.get_scheduler())); + } + }(context, scope)); + + context.run(); + + +}