diff --git a/example/example.vcxproj b/example/example.vcxproj deleted file mode 100644 index 8c79cf4f..00000000 --- a/example/example.vcxproj +++ /dev/null @@ -1,148 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {E85500EF-94CC-483E-97E3-216F7B1B127D} - example - 10.0.19041.0 - - - - Application - true - v143 - MultiByte - - - Application - false - v143 - true - MultiByte - - - Application - true - v143 - MultiByte - - - Application - false - v143 - true - MultiByte - - - - - - - - - - - - - - - - - - - - - ..\thirdparty\async_simple;..\thirdparty\asio;$(IncludePath) - - - D:\asio-1.12.2\include;$(IncludePath) - - - ..\thirdparty\async_simple;..\thirdparty\asio;D:\Program Files\OpenSSL-Win64\include;$(IncludePath) - D:\Program Files\OpenSSL-Win64\lib;$(LibraryPath) - - - - Level3 - Disabled - true - true - stdcpp17 - 4996 - ASIO_STANDALONE;%(PreprocessorDefinitions) - - - - - Level3 - Disabled - true - true - stdcpp20 - 4996 - /bigobj %(AdditionalOptions) - ASIO_STANDALONE;ASYNC_SIMPLE_HAS_NOT_AIO;CINATRA_ENABLE_SSL;%(PreprocessorDefinitions) - - - libssl_static.lib;libcrypto_static.lib;%(AdditionalDependencies) - - - - - Level3 - MaxSpeed - true - true - true - true - stdcpp17 - 4996 - ASIO_STANDALONE;%(PreprocessorDefinitions) - - - true - true - - - - - Level3 - MaxSpeed - true - true - true - true - stdcpp17 - 4996 - __SSE4_2__;CINATRA_ENABLE_SSL;%(PreprocessorDefinitions) - - - true - true - - - - - - - - - \ No newline at end of file diff --git a/example/main.cpp b/example/main.cpp index 48e576d8..89fb722d 100644 --- a/example/main.cpp +++ b/example/main.cpp @@ -1,21 +1,25 @@ +#include +#include #include +#include +#include +#include +#include #include "../include/cinatra.hpp" -#include "cinatra/response_cv.hpp" using namespace cinatra; +using namespace std::chrono_literals; -template -inline void print(Args... args) { - ((std::cout << args << ' '), ...); - std::cout << "\n"; -} - -inline void print(const std::error_code &ec) { - print(ec.value(), ec.message()); +void create_file(std::string filename, size_t file_size = 64) { + std::ofstream file(filename, std::ios::binary); + if (file) { + std::string str(file_size, 'A'); + file.write(str.data(), str.size()); + } } -struct log_t { +struct log_t : public base_aspect { bool before(coro_http_request &, coro_http_response &) { std::cout << "before log" << std::endl; return true; @@ -28,245 +32,353 @@ struct log_t { } }; -struct person { - void foo(coro_http_request &, coro_http_response &res) { - std::cout << i << std::endl; - res.set_status(status_type::ok); - } - - void foo1(coro_http_request &, coro_http_response &res) { - std::cout << i << std::endl; - res.set_status(status_type::ok); - } - - int i = 0; -}; - -void test_ssl_server() { -#ifdef CINATRA_ENABLE_SSL - // you should open macro CINATRA_ENABLE_SSL at first - http_ssl_server server(2); - - server.set_ssl_conf({"server.crt", "server.key"}); - int r = server.listen("0.0.0.0", "9001"); - if (r < 0) { - return; - } - - server.set_http_handler( - "/", [](coro_http_request &req, coro_http_response &res) { - auto str = req.get_conn()->remote_address(); - res.set_status_and_content(status_type::ok, "hello world from 9001"); - }); - - server.run(); -#endif -} - -#ifdef CINATRA_ENABLE_SSL -void test_coro_http_client() { - using namespace cinatra; - coro_http_client client{}; - client.init_ssl("bing.com", "../../include/cinatra", "server.crt"); - auto data = client.get("https://www.bing.com"); - std::cout << data.resp_body << "\n"; - data = client.get("https://www.bing.com"); - std::cout << data.resp_body << "\n"; -} -#endif +async_simple::coro::Lazy byte_ranges_download() { + create_file("test_multiple_range.txt", 64); + coro_http_server server(1, 8090); + server.set_static_res_dir("", ""); + server.async_start(); + std::this_thread::sleep_for(200ms); -void test_sync_client() { + std::string uri = "http://127.0.0.1:8090/test_multiple_range.txt"; { - std::string uri = "http://www.baidu.com"; - coro_http_client client{}; - auto result = client.get(uri); - assert(!result.net_err); - print(result.status); + std::string filename = "test1.txt"; + std::error_code ec{}; + std::filesystem::remove(filename, ec); - result = client.post(uri, "hello", req_content_type::json); - print(result.status); + coro_http_client client{}; + resp_data result = co_await client.async_download(uri, filename, "1-10"); + assert(result.status == 206); + assert(std::filesystem::file_size(filename) == 10); + + filename = "test2.txt"; + std::filesystem::remove(filename, ec); + result = co_await client.async_download(uri, filename, "10-15"); + assert(result.status == 206); + assert(std::filesystem::file_size(filename) == 6); } { coro_http_client client{}; - std::string uri = "http://cn.bing.com"; - auto result = client.get(uri); - assert(!result.net_err); - print(result.status); + std::string uri = "http://127.0.0.1:8090/test_multiple_range.txt"; - result = client.post(uri, "hello", req_content_type::json); - print(result.status); + client.add_header("Range", "bytes=1-10,20-30"); + auto result = client.get(uri); + assert(result.status == 206); + assert(result.resp_body.size() == 21); + + std::string filename = "test_ranges.txt"; + client.add_header("Range", "bytes=0-10,21-30"); + result = co_await client.async_download(uri, filename); + assert(result.status == 206); + assert(fs::file_size(filename) == 21); } } -async_simple::coro::Lazy test_async_client(coro_http_client &client) { - std::string uri = "http://www.baidu.com"; - auto data = co_await client.async_get(uri); - print(data.status); +async_simple::coro::Lazy chunked_upload1(coro_http_client &client) { + std::string filename = "test.txt"; + create_file(filename, 1010); - data = co_await client.async_get(uri); - print(data.status); + coro_io::coro_file file{}; + co_await file.async_open(filename, coro_io::flags::read_only); - data = co_await client.async_post(uri, "hello", req_content_type::string); - print(data.status); -} + std::string buf; + detail::resize(buf, 100); -async_simple::coro::Lazy test_async_ssl_client(coro_http_client &client) { -#ifdef CINATRA_ENABLE_SSL - std::string uri2 = "https://www.baidu.com"; - std::string uri3 = "https://cn.bing.com"; - client.init_ssl("bing.com", "../../include/cinatra", "server.crt"); - auto data = co_await client.async_get(uri2); - print(data.status); - - data = co_await client.async_get(uri3); - print(data.status); -#endif - co_return; + auto fn = [&file, &buf]() -> async_simple::coro::Lazy { + auto [ec, size] = co_await file.async_read(buf.data(), buf.size()); + co_return read_result{buf, file.eof(), ec}; + }; + + auto result = co_await client.async_upload_chunked( + "http://127.0.0.1:9001/chunked"sv, http_method::POST, std::move(fn)); + co_return result; } -async_simple::coro::Lazy test_download() { +async_simple::coro::Lazy chunked_upload_download() { + cinatra::coro_http_server server(1, 9001); + server.set_http_handler( + "/chunked", + [](coro_http_request &req, + coro_http_response &resp) -> async_simple::coro::Lazy { + assert(req.get_content_type() == content_type::chunked); + chunked_result result{}; + std::string content; + + while (true) { + result = co_await req.get_conn()->read_chunked(); + if (result.ec) { + co_return; + } + if (result.eof) { + break; + } + + content.append(result.data); + } + + std::cout << "content size: " << content.size() << "\n"; + std::cout << content << "\n"; + resp.set_format_type(format_type::chunked); + resp.set_status_and_content(status_type::ok, "chunked ok"); + }); + + server.set_http_handler( + "/write_chunked", + [](coro_http_request &req, + coro_http_response &resp) -> async_simple::coro::Lazy { + resp.set_format_type(format_type::chunked); + bool ok; + if (ok = co_await resp.get_conn()->begin_chunked(); !ok) { + co_return; + } + + std::vector vec{"hello", " world", " ok"}; + + for (auto &str : vec) { + if (ok = co_await resp.get_conn()->write_chunked(str); !ok) { + co_return; + } + } + + ok = co_await resp.get_conn()->end_chunked(); + }); + + server.async_start(); + std::this_thread::sleep_for(200ms); + coro_http_client client{}; - std::string uri = - "http://www.httpwatch.com/httpgallery/chunked/chunkedimage.aspx"; - std::string filename = "test.jpg"; - - std::error_code ec{}; - std::filesystem::remove(filename, ec); - auto r = co_await client.async_download(uri, filename); - assert(!r.net_err); + auto r = co_await chunked_upload1(client); assert(r.status == 200); - std::cout << "download finished\n"; + assert(r.resp_body == "chunked ok"); + + auto ss = std::make_shared(); + *ss << "hello world"; + auto result = co_await client.async_upload_chunked( + "http://127.0.0.1:9001/chunked"sv, http_method::POST, ss); + assert(result.status == 200); + assert(result.resp_body == "chunked ok"); + + result = co_await client.async_get("http://127.0.0.1:9001/write_chunked"); + assert(result.status == 200); + assert(result.resp_body == "hello world ok"); } -async_simple::coro::Lazy test_upload() { - std::string uri = "http://example.com/"; - coro_http_client client{}; - auto result = - co_await client.async_upload_multipart(uri, "test", "yourfile.jpg"); - print(result.status); - std::cout << "upload finished\n"; - - client.add_str_part("hello", "coro_http_client"); - client.add_file_part("test", "yourfile.jpg"); - result = co_await client.async_upload_multipart(uri, "test", "yourfile.jpg"); - print(result.status); - std::cout << "upload finished\n"; -} +async_simple::coro::Lazy use_websocket() { + coro_http_server server(1, 9001); + server.set_http_handler( + "/ws_echo", + [](coro_http_request &req, + coro_http_response &resp) -> async_simple::coro::Lazy { + assert(req.get_content_type() == content_type::websocket); + websocket_result result{}; + while (true) { + result = co_await req.get_conn()->read_websocket(); + if (result.ec) { + break; + } + + if (result.type == ws_frame_type::WS_CLOSE_FRAME) { + std::cout << "close frame\n"; + break; + } + + if (result.type == ws_frame_type::WS_TEXT_FRAME || + result.type == ws_frame_type::WS_BINARY_FRAME) { + std::cout << result.data << "\n"; + } + + auto ec = co_await req.get_conn()->write_websocket(result.data); + if (ec) { + break; + } + } + }); + server.async_start(); + std::this_thread::sleep_for(300ms); // wait for server start -async_simple::coro::Lazy test_websocket() { coro_http_client client{}; client.on_ws_close([](std::string_view reason) { - std::cout << "web socket close " << reason << std::endl; + std::cout << reason << "\n"; + assert(reason == "normal close"); }); client.on_ws_msg([](resp_data data) { if (data.net_err) { std::cout << data.net_err.message() << "\n"; return; } - std::cout << data.resp_body << std::endl; + assert(data.resp_body == "hello websocket" || + data.resp_body == "test again"); }); - bool r = co_await client.async_ws_connect("ws://localhost:8090/ws"); + bool r = co_await client.async_ws_connect("ws://127.0.0.1:9001/ws_echo"); if (!r) { co_return; } auto result = co_await client.async_send_ws("hello websocket"); // mask as default. - std::cout << result.status << "\n"; + assert(!result.net_err); result = co_await client.async_send_ws("test again", /*need_mask = */ false); - std::cout << result.status << "\n"; - result = co_await client.async_send_ws_close("ws close"); - std::cout << result.status << "\n"; + assert(!result.net_err); } -void test_smtp_client() { - asio::io_context io_context; -#ifdef CINATRA_ENABLE_SSL - auto client = cinatra::smtp::get_smtp_client(io_context); -#else - auto client = cinatra::smtp::get_smtp_client(io_context); -#endif - smtp::email_server server{}; - server.server = "smtp.163.com"; - server.port = client.IS_SSL ? "465" : "25"; - server.user = "your_email@163.com"; - server.password = "your_email_password"; - - smtp::email_data data{}; - data.filepath = ""; // some file as attachment. - data.from_email = "your_email@163.com"; - data.to_email.push_back("to_some_email@163.com"); - // data.to_email.push_back("to_more_email@example.com"); - data.subject = "it is a test from cinatra smtp"; - data.text = "Hello cinatra smtp client"; - - client.set_email_server(server); - client.set_email_data(data); - - client.start(); - - std::error_code ec; - io_context.run(ec); +async_simple::coro::Lazy static_file_server() { + std::string filename = "temp.txt"; + create_file(filename, 64); + + coro_http_server server(1, 9001); + + std::string virtual_path = "download"; + std::string files_root_path = ""; // current path + server.set_static_res_dir( + virtual_path, + files_root_path); // set this before server start, if you add new files, + // you need restart the server. + server.async_start(); + std::this_thread::sleep_for(300ms); // wait for server start + + coro_http_client client{}; + auto result = + co_await client.async_get("http://127.0.0.1:9001/download/temp.txt"); + assert(result.status == 200); + assert(result.resp_body.size() == 64); } -class qps { - public: - void increase() { counter_.fetch_add(1, std::memory_order_release); } - - qps() : counter_(0) { - thd_ = std::thread([this] { - while (!stop_) { - size_t current = counter_.load(std::memory_order_acquire); - std::cout << "qps: " << current - last_ << '\n'; - last_ = current; - std::this_thread::sleep_for(std::chrono::seconds(1)); - // counter_.store(0, std::memory_order_release); - } - }); - } +async_simple::coro::Lazy use_aspects() { + coro_http_server server(1, 9001); + std::vector> aspects{std::make_shared()}; + server.set_http_handler( + "/get", + [](coro_http_request &req, coro_http_response &resp) { + resp.set_status_and_content(status_type::ok, "ok"); + }, + aspects); - ~qps() { - stop_ = true; - thd_.join(); - } + server.async_start(); + std::this_thread::sleep_for(300ms); // wait for server start - private: - bool stop_ = false; - std::thread thd_; - std::atomic counter_; - uint32_t last_ = 0; -}; + coro_http_client client{}; + auto result = co_await client.async_get("http://127.0.0.1:9001/get"); + assert(result.status == 200); -int main() { - // test_coro_http_client(); - // test_smtp_client(); - { - test_sync_client(); - coro_http_client client{}; - async_simple::coro::syncAwait(test_async_client(client)); + co_return; +} - coro_http_client ssl_client{}; - async_simple::coro::syncAwait(test_async_ssl_client(ssl_client)); +struct person_t { + void foo(coro_http_request &, coro_http_response &res) { + res.set_status_and_content(status_type::ok, "ok"); } +}; - // test_ssl_server(); - // test_download(); - coro_http_server server(std::thread::hardware_concurrency(), 8090); +async_simple::coro::Lazy basic_usage() { + coro_http_server server(1, 9001); + server.set_http_handler( + "/get", [](coro_http_request &req, coro_http_response &resp) { + resp.set_status_and_content(status_type::ok, "ok"); + }); - // server.on_connection([](auto conn) { return true; }); - server.set_http_handler( - "/", [](coro_http_request &, coro_http_response &res) mutable { - res.set_status_and_content(status_type::ok, "hello world"); + server.set_http_handler( + "/coro", + [](coro_http_request &req, + coro_http_response &resp) -> async_simple::coro::Lazy { + resp.set_status_and_content(status_type::ok, "ok"); + co_return; }); server.set_http_handler( - "/plaintext", [](coro_http_request &, coro_http_response &res) { - res.set_status_and_content(status_type::ok, "Hello, World!"); + "/in_thread_pool", + [](coro_http_request &req, + coro_http_response &resp) -> async_simple::coro::Lazy { + // will respose in another thread. + co_await coro_io::post([&] { + // do your heavy work here when finished work, response. + resp.set_status_and_content(status_type::ok, "ok"); + }); + }); + + server.set_http_handler( + "/post", [](coro_http_request &req, coro_http_response &resp) { + auto req_body = req.get_body(); + resp.set_status_and_content(status_type::ok, std::string{req_body}); }); + server.set_http_handler( + "/headers", [](coro_http_request &req, coro_http_response &resp) { + auto name = req.get_header_value("name"); + auto age = req.get_header_value("age"); + assert(name == "tom"); + assert(age == "20"); + resp.set_status_and_content(status_type::ok, "ok"); + }); + + server.set_http_handler( + "/query", [](coro_http_request &req, coro_http_response &resp) { + auto name = req.get_query_value("name"); + auto age = req.get_query_value("age"); + assert(name == "tom"); + assert(age == "20"); + resp.set_status_and_content(status_type::ok, "ok"); + }); + + server.set_http_handler( + "/users/:userid/subscriptions/:subid", + [](coro_http_request &req, coro_http_response &response) { + assert(req.params_["userid"] == "ultramarines"); + assert(req.params_["subid"] == "guilliman"); + response.set_status_and_content(status_type::ok, "ok"); + }); + + person_t person{}; + server.set_http_handler("/person", &person_t::foo, person); + server.async_start(); + std::this_thread::sleep_for(300ms); // wait for server start + + coro_http_client client{}; + auto result = co_await client.async_get("http://127.0.0.1:9001/get"); + assert(result.status == 200); + assert(result.resp_body == "ok"); + for (auto [key, val] : result.resp_headers) { + std::cout << key << ": " << val << "\n"; + } + result = co_await client.async_get("/coro"); + assert(result.status == 200); + + result = co_await client.async_get("/in_thread_pool"); + assert(result.status == 200); + + result = co_await client.async_post("/post", "post string", + req_content_type::string); + assert(result.status == 200); + assert(result.resp_body == "post string"); + + client.add_header("name", "tom"); + client.add_header("age", "20"); + result = co_await client.async_get("/headers"); + assert(result.status == 200); + + result = co_await client.async_get("/query?name=tom&age=20"); + assert(result.status == 200); + + result = co_await client.async_get( + "http://127.0.0.1:9001/users/ultramarines/subscriptions/guilliman"); + assert(result.status == 200); + + // make sure you have install openssl and enable CINATRA_ENABLE_SSL +#ifdef CINATRA_ENABLE_SSL + coro_http_client client2{}; + result = co_await client2.async_get("https://baidu.com"); + assert(result.status == 200); +#endif +} + +int main() { + async_simple::coro::syncAwait(basic_usage()); + async_simple::coro::syncAwait(use_aspects()); + async_simple::coro::syncAwait(static_file_server()); + async_simple::coro::syncAwait(use_websocket()); + async_simple::coro::syncAwait(chunked_upload_download()); + async_simple::coro::syncAwait(byte_ranges_download()); return 0; } \ No newline at end of file