From 1841a592d6fc1fb3ab949ddcc2a0d496fcdfee7d Mon Sep 17 00:00:00 2001 From: Klemens Date: Wed, 21 Dec 2022 15:30:58 +0800 Subject: [PATCH] Minimalistic method examples. --- doc/qbk/02_examples/_examples.qbk | 4 +- example/http/client/CMakeLists.txt | 2 + example/http/client/Jamfile | 2 + example/http/client/methods/CMakeLists.txt | 23 ++ example/http/client/methods/Jamfile | 15 ++ .../client/methods/http_client_methods.cpp | 225 ++++++++++++++++++ 6 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 example/http/client/methods/CMakeLists.txt create mode 100644 example/http/client/methods/Jamfile create mode 100644 example/http/client/methods/http_client_methods.cpp diff --git a/doc/qbk/02_examples/_examples.qbk b/doc/qbk/02_examples/_examples.qbk index c5889a8b..a5545185 100644 --- a/doc/qbk/02_examples/_examples.qbk +++ b/doc/qbk/02_examples/_examples.qbk @@ -51,7 +51,9 @@ used to evaluate robustness. All asynchronous clients support timeouts. ][ [HTTP json_body (synchronous)] [[path_link example/http/client/body/json_body.hpp example/http/client/body/json_client.cpp]] - +][ + [HTTP client for all methods (synchronous)] + [[path_link example/http/client/methods/http_client_methods.hpp example/http/client/methods/http_client_methods.cpp]] ]] These WebSocket clients connect to a diff --git a/example/http/client/CMakeLists.txt b/example/http/client/CMakeLists.txt index 5b08610e..e052a9b3 100644 --- a/example/http/client/CMakeLists.txt +++ b/example/http/client/CMakeLists.txt @@ -13,6 +13,7 @@ add_subdirectory (coro) add_subdirectory (crawl) add_subdirectory (sync) add_subdirectory (body) +add_subdirectory (methods) if (OPENSSL_FOUND) add_subdirectory (async-ssl) @@ -21,3 +22,4 @@ if (OPENSSL_FOUND) add_subdirectory (coro-ssl) add_subdirectory (sync-ssl) endif() + diff --git a/example/http/client/Jamfile b/example/http/client/Jamfile index cfcced01..bfe3d7f3 100644 --- a/example/http/client/Jamfile +++ b/example/http/client/Jamfile @@ -20,3 +20,5 @@ build-project coro-ssl ; build-project sync-ssl ; build-project body ; + +build-project methods ; diff --git a/example/http/client/methods/CMakeLists.txt b/example/http/client/methods/CMakeLists.txt new file mode 100644 index 00000000..cbd5e51b --- /dev/null +++ b/example/http/client/methods/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# +# Official repository: https://github.com/boostorg/beast +# + +GroupSources(include/boost/beast beast) +GroupSources(example/http/client/methods "/") + +add_executable (http-client-methods + ${BOOST_BEAST_FILES} + Jamfile + http_client_methods.cpp +) + +target_link_libraries(http-client-methods + lib-asio + lib-beast) + +set_property(TARGET http-client-methods PROPERTY FOLDER "example-http-client-methods") diff --git a/example/http/client/methods/Jamfile b/example/http/client/methods/Jamfile new file mode 100644 index 00000000..e16fd57e --- /dev/null +++ b/example/http/client/methods/Jamfile @@ -0,0 +1,15 @@ +# +# Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# +# Official repository: https://github.com/boostorg/beast +# + +exe http-client-methods : + http_client_methods.cpp + : + coverage:no + ubasan:no + ; diff --git a/example/http/client/methods/http_client_methods.cpp b/example/http/client/methods/http_client_methods.cpp new file mode 100644 index 00000000..f48cf99b --- /dev/null +++ b/example/http/client/methods/http_client_methods.cpp @@ -0,0 +1,225 @@ +// +// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/beast +// + +//------------------------------------------------------------------------------ +// +// Example: HTTP client, synchronous for every method on httpbin +// +//------------------------------------------------------------------------------ + +//[example_http_client_methods + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace beast = boost::beast; // from +namespace http = beast::http; // from +namespace net = boost::asio; // from +using tcp = net::ip::tcp; // from + + +// perform a get request +void do_get(beast::tcp_stream & stream, + http::request & req, + beast::flat_buffer buffer, + http::response & res) +{ + req.target("/get"); + req.method(beast::http::verb::get); + http::write(stream, req); + http::read(stream, buffer, res); +} + +// perform a head request +void do_head(beast::tcp_stream & stream, + http::request & req, + beast::flat_buffer buffer, + http::response & res) +{ + // we reuse the get endpoint + req.target("/get"); + req.method(beast::http::verb::head); + http::write(stream, req); + + /* the head response will send back a content-length + * without a body. The other requests don't set content-length when not + * sending a body back. + * + * the response parser doesn't know that we sent head, + * so we need to manually make sure we're only reading the header + * otherwise we're waiting forever for data. + */ + + http::response_parser p; + http::read_header(stream, buffer, p); + // move the result over + res = p.release(); +} + + +// perform a patch request +void do_patch(beast::tcp_stream & stream, + http::request & req, + beast::flat_buffer buffer, + http::response & res) +{ + req.target("/patch"); + req.method(beast::http::verb::patch); + req.body() = "Some random patch data"; + req.prepare_payload(); // set content-length based on the body + http::write(stream, req); + http::read(stream, buffer, res); +} + + +// perform a put request +void do_put(beast::tcp_stream & stream, + http::request & req, + beast::flat_buffer buffer, + http::response & res) +{ + req.target("/put"); + req.method(beast::http::verb::put); + req.body() = "Some random put data"; + req.prepare_payload(); // set content-length based on the body + http::write(stream, req); + http::read(stream, buffer, res); +} + +// perform a post request +void do_post(beast::tcp_stream & stream, + http::request & req, + beast::flat_buffer buffer, + http::response & res) +{ + req.target("/post"); + req.method(beast::http::verb::post); + req.body() = "Some random post data"; + req.prepare_payload(); // set content-length based on the body + http::write(stream, req); + http::read(stream, buffer, res); +} + + +// perform a delete request +void do_delete(beast::tcp_stream & stream, + http::request & req, + beast::flat_buffer buffer, + http::response & res) +{ + req.target("/delete"); + req.method(beast::http::verb::delete_); + // NOTE: delete doesn't require a body + req.body() = "Some random delete data"; + req.prepare_payload(); // set content-length based on the body + http::write(stream, req); + http::read(stream, buffer, res); +} + + +// Performs an HTTP request against httpbin.cpp.al and prints request & response +int main(int argc, char** argv) +{ + try + { + // Check command line arguments. + if(argc != 2) + { + std::cerr << + "Usage: http-client-method \n" << + "Example:\n" << + " http-client-method get\n" << + " http-client-method post\n"; + return EXIT_FAILURE; + } + + for (char * c = argv[1]; *c != '\0'; c++) + *c = static_cast(std::tolower(*c)); + beast::string_view method{argv[1]}; + + // The io_context is required for all I/O + net::io_context ioc; + + // These objects perform our I/O + tcp::resolver resolver(ioc); + beast::tcp_stream stream(ioc); + + // Look up the domain name + auto const results = resolver.resolve("httpbin.cpp.al", "http"); + + // Make the connection on the IP address we get from a lookup + stream.connect(results); + + + // Set up an HTTP GET request message + http::request req; + req.set(http::field::host, "httpbin.cpp.al"); + req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); + + + // This buffer is used for reading and must be persisted + beast::flat_buffer buffer; + + // Declare a container to hold the response + http::response res; + + if (method == "get") + do_get(stream, req, buffer, res); + else if (method == "head") + do_head(stream, req, buffer, res); + else if (method == "patch") + do_patch(stream, req, buffer, res); + else if (method == "put") + do_put(stream, req, buffer, res); + else if (method == "post") + do_post(stream, req, buffer, res); + else if (method == "delete") + do_delete(stream, req, buffer, res); + else + { + std::cerr << "Unknown method: " << method << std::endl; + return EXIT_FAILURE; + } + + // Write the message to standard out + std::cout << "Request send:\n-----------------------------\n" + << req << std::endl; + + + // Write the message to standard out + std::cout << "\n\nResponse received:\n-----------------------------\n" + << res << std::endl; + + // Gracefully close the socket + beast::error_code ec; + stream.socket().shutdown(tcp::socket::shutdown_both, ec); + + // not_connected happens sometimes + // so don't bother reporting it. + // + if(ec && ec != beast::errc::not_connected) + throw beast::system_error{ec}; + + // If we get here then the connection is closed gracefully + } + catch(std::exception const& e) + { + std::cerr << "Error: " << e.what() << std::endl; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +//]