[beast] help with http::dynamic_body and http::buffer_body
Hello, After using boost::asio for several years I thought, I might give beast a try. I need to write a http server processing a POST request with an upload of a big file. So I thought, I could start with libs/beast/example/http/server/async. I soon found the function handle_request() and that I have to add something like if (req.method() == http::verb::post && req.target()=="/xxx" ) to catch the http POST transaction. But since I have to process the upload of a large file, I thought that using a http::string_body might not be the best choice. I found several other body types in the documentation. I thought buffer_body ought to be fine, since the documentation says: ‘This allows for serialization of body data coming from external sources, and incremental parsing of message body content using a fixed size buffer.’. incremental processing of the input stream is what I need. But now, I have no clue, how to change the example to use a buffer_body. I changed http::request<http::string_body> req_ to http::request<http::buffer_body> req_. But now I must process the request message in chunks. How do I get my callback invoked for each chunk? Without additional changes, I get a read error: need buffer. But the documentation on buffer_body does not help me. Buffer_body does not seem to have any methods, that I can call. It does have a member type reader, that is documented as implementation-defined. So there is nothing I can do with this reader. ☹ There is a member type value_type, that does have data, size and more mebers, but I have no idea, how I could et hold of a variable of this type containing a part of my message body. The error message might indicate, that I need to provide a fixed size buffer to the buffer_body, but I have no idea, how to do this. I tried to add the following code to do_read(): req_.body().data = my_buffer; req_.body().size = sizeof(my_buffer); It compiles, so req_body() seems to be the way to get hold on a http::buffer_body::value_type, but simply initializing the two variables does not seem to be sufficient. I also tried to set req_.body().more to false or to true, but it didn’t change anything. I then thought, I might try a dynamic_body. This would store the entire upload in memory, but perhaps I get more familiar with the beat framework, when I try to use this body. And in deed, I managed to write some code, that shows all request headers and the request body on stdout. To get the body, I had to copy the buffer contents into a std::string: std::string body{ boost::asio::buffers_begin(req.body().data()), boost::asio::buffers_end(req.body().data()) }; Since large files have to be handled by my code, this additional copy of the data is to be avoided. So I tried to iterate the body using the iterators: const auto begin = boost::asio::buffers_begin(req.body().data()); const auto end = boost::asio::buffers_end(req.body().data()); for (auto it = begin; it != end; it++) putchar(*it); While it runs fine on the windows box, the loop crashes on linux in the 512th iteration in it++. Program received signal SIGSEGV, Segmentation fault. 0x0000555555592432 in boost::intrusive::list_node_traits<void*>::get_previous (n=@0x7fffffffc118: 0x1ff) at ../boost/boost/intrusive/detail/list_node.hpp:54 54 { return n->prev_; } (gdb) print n $1 = (const boost::intrusive::list_node_traits<void*>::node_ptr &) @0x7fffffffc118: 0x1ff (gdb) Here is the call stack: #0 0x0000555555592432 in boost::intrusive::list_node_traits<void*>::get_previous (n=@0x7fffffffc118: 0x1ff) at ../boost/boost/intrusive/detail/list_node.hpp:54 #1 boost::intrusive::list_iterator<boost::intrusive::bhtraits<boost::beast::basic_multi_buffer<std::allocator<char> >::element, boost::intrusive::list_node_traits<void*>, (boost::intrusive::link_mode_type)0, boost::intrusive::dft_tag, 1u>, true>::operator-- (this=0x7fffffffc118) at ../boost/boost/intrusive/detail/list_iterator.hpp:107 #2 std::__advance<boost::intrusive::list_iterator<boost::intrusive::bhtraits<boost::beast::basic_multi_buffer<std::allocator<char> >::element, boost::intrusive::list_node_traits<void*>, (boost::intrusive::link_mode_type)0, boost::intrusive::dft_tag, 1u>, true>, long> (__i=..., __n=0) at /usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_iterator_base_funcs.h:169 #3 0x000055555558daf0 in std::advance<boost::intrusive::list_iterator<boost::intrusive::bhtraits<boost::beast::basic_multi_buffer<std::allocator<char> >::element, boost::intrusive::list_node_traits<void*>, (boost::intrusive::link_mode_type)0, boost::intrusive::dft_tag, 1u>, true>, long> (__i=..., __n=-1) at /usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_iterator_base_funcs.h:206 #4 0x0000555555589254 in std::prev<boost::intrusive::list_iterator<boost::intrusive::bhtraits<boost::beast::basic_multi_buffer<std::allocator<char> >::element, boost::intrusive::list_node_traits<void*>, (boost::intrusive::link_mode_type)0, boost::intrusive::dft_tag, 1u>, true> > (__x=..., __n=1) at /usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_iterator_base_funcs.h:230 #5 0x0000555555589608 in boost::beast::basic_multi_buffer<std::allocator<char> >::subrange<true>::const_iterator::operator* (this=0x7fffffffc4f8) at ../boost/boost/beast/core/impl/multi_buffer.hpp:384 #6 0x0000555555589b4b in boost::asio::buffers_iterator<boost::beast::basic_multi_buffer<std::allocator<char> >::subrange<true>, char>::increment (this=0x7fffffffc4d0) at ../boost/boost/asio/buffers_iterator.hpp:370 #7 0x0000555555584894 in boost::asio::buffers_iterator<boost::beast::basic_multi_buffer<std::allocator<char> >::subrange<true>, char>::operator++ (this=0x7fffffffc4d0) at ../boost/boost/asio/buffers_iterator.hpp:229 #8 0x000055555557e7c0 in boost::asio::buffers_iterator<boost::beast::basic_multi_buffer<std::allocator<char> >::subrange<true>, char>::operator++ (this=0x7fffffffc4d0) at ../boost/boost/asio/buffers_iterator.hpp:237 #9 0x0000555555579ba8 in handle_request<boost::beast::http::basic_dynamic_body<boost::beast::basic_multi_buffer<std::allocator<char> > >, std::allocator<char>, session::send_lambda&> (doc_root=..., req=..., send=...) at t.cpp:164 I hope anybody can give my a hint on how to use this beast. Right now it feels that it might be easier to write the http server using plain boost::asio. Klebsch Mario Funktion | R&D Tel: +49 (0) 531 38 701 718 Raum: Braunschweig, E20 Diese E-Mail und die an sie angehängten Dateien sind ausschließlich für Personen oder Institutionen bestimmt, deren Namen oben aufgeführt sind. Sie können Informationen enthalten, die durch das Berufsgeheimnis geschützt sind und deren Weitergabe strengstens untersagt ist. Jede elektronische Nachricht kann geändert werden. ACTIA lehnt jede Verantwortung für diese Nachricht ab. Der Inhalt dieser Nachricht stellt keine Verpflichtung seitens unseres Unternehmens dar. Wenn Sie kein Empfänger sind, weisen wir Sie darauf hin, dass das Lesen, Vervielfältigen oder Verteilen strengstens untersagt ist. Wir bitten Sie daher, uns umgehend über diesen Brief zu informieren und diese Nachricht sowie eventuell beigefügte Unterlagen aus Ihrem Postfach zu löschen. Danke. This e-mail and the files attached to it are intended exclusively for persons or institutions whose names are listed above. They may contain information that is protected by professional secrecy and the disclosure of which is strictly prohibited. Any electronic message can be modified. ACTIA declines all responsibility for this message. The content of this message does not represent a commitment on the part of our company. If you are not a recipient, we would like to point out that reading, copying or distribution is strictly prohibited. We therefore ask you to inform us immediately about this letter and to delete this message and any accompanying documents from your mailbox. Thank you.
Interesting. Would you mind sharing your code (perhaps in a git repo) so that I can reproduce, debug and advise? You can email me direct on hodges.r@gmail.com R On Mon, 26 Apr 2021 at 16:26, Klebsch, Mario via Boost-users < boost-users@lists.boost.org> wrote:
Hello,
After using boost::asio for several years I thought, I might give beast a try. I need to write a http server processing a POST request with an upload of a big file. So I thought, I could start with libs/beast/example/http/server/async. I soon found the function handle_request() and that I have to add something like
if (req.method() == http::verb::post && req.target()=="/xxx" )
to catch the http POST transaction. But since I have to process the upload of a large file, I thought that using a http::string_body might not be the best choice. I found several other body types in the documentation. I thought
buffer_body ought to be fine, since the documentation says: ‘This allows for serialization of body data coming from external sources, and incremental parsing of message body content using a fixed size buffer.’.
incremental processing of the input stream is what I need. But now, I have no clue, how to change the example to use a buffer_body.
I changed http::request<http::string_body> req_ to http::request<http::buffer_body> req_. But now I must process the request message in chunks.
How do I get my callback invoked for each chunk?
Without additional changes, I get a read error: need buffer.
But the documentation on buffer_body does not help me. Buffer_body does not seem to have any methods, that I can call.
It does have a member type reader, that is documented as implementation-defined. So there is nothing I can do with this reader. ☹
There is a member type value_type, that does have data, size and more mebers, but I have no idea, how I could et hold of a variable of this type containing a part of my message body.
The error message might indicate, that I need to provide a fixed size buffer to the buffer_body, but I have no idea, how to do this.
I tried to add the following code to do_read():
req_.body().data = my_buffer;
req_.body().size = sizeof(my_buffer);
It compiles, so req_body() seems to be the way to get hold on a http::buffer_body::value_type, but simply initializing the two variables does not seem to be sufficient. I also tried to set req_.body().more to false or to true, but it didn’t change anything.
I then thought, I might try a dynamic_body. This would store the entire upload in memory, but perhaps I get more familiar with the beat framework, when I try to use this body.
And in deed, I managed to write some code, that shows all request headers and the request body on stdout. To get the body, I had to copy the buffer contents into a std::string:
std::string body{ boost::asio::buffers_begin(req.body().data()),
boost::asio::buffers_end(req.body().data()) };
Since large files have to be handled by my code, this additional copy of the data is to be avoided. So I tried to iterate the body using the iterators:
const auto begin = boost::asio::buffers_begin(req.body().data());
const auto end = boost::asio::buffers_end(req.body().data());
for (auto it = begin; it != end; it++)
putchar(*it);
While it runs fine on the windows box, the loop crashes on linux in the 512 th iteration in it++.
Program received signal SIGSEGV, Segmentation fault.
0x0000555555592432 in boost::intrusive::list_node_traits<void*>::get_previous (n=@0x7fffffffc118: 0x1ff) at ../boost/boost/intrusive/detail/list_node.hpp:54
54 { return n->prev_; }
(gdb) print n
$1 = (const boost::intrusive::list_node_traits<void*>::node_ptr &) @0x7fffffffc118: 0x1ff
(gdb)
Here is the call stack:
#0 0x0000555555592432 in boost::intrusive::list_node_traits<void*>::get_previous (n=@0x7fffffffc118: 0x1ff) at ../boost/boost/intrusive/detail/list_node.hpp:54
::element, boost::intrusive::list_node_traits<void*>, (boost::intrusive::link_mode_type)0, boost::intrusive::dft_tag, 1u>,
#1 boost::intrusive::list_iterator<boost::intrusive::bhtraits<boost::beast::basic_multi_buffer<std::allocator<char> true>::operator-- (this=0x7fffffffc118) at ../boost/boost/intrusive/detail/list_iterator.hpp:107
#2 std::__advance<boost::intrusive::list_iterator<boost::intrusive::bhtraits<boost::beast::basic_multi_buffer<std::allocator<char>
::element, boost::intrusive::list_node_traits<void*>, (boost::intrusive::link_mode_type)0, boost::intrusive::dft_tag, 1u>, true>, long> (__i=..., __n=0) at /usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_iterator_base_funcs.h:169
#3 0x000055555558daf0 in std::advance<boost::intrusive::list_iterator<boost::intrusive::bhtraits<boost::beast::basic_multi_buffer<std::allocator<char>
::element, boost::intrusive::list_node_traits<void*>, (boost::intrusive::link_mode_type)0, boost::intrusive::dft_tag, 1u>, true>, long> (__i=..., __n=-1) at /usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_iterator_base_funcs.h:206
#4 0x0000555555589254 in std::prev<boost::intrusive::list_iterator<boost::intrusive::bhtraits<boost::beast::basic_multi_buffer<std::allocator<char>
::element, boost::intrusive::list_node_traits<void*>, (boost::intrusive::link_mode_type)0, boost::intrusive::dft_tag, 1u>, true> (__x=..., __n=1) at /usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_iterator_base_funcs.h:230
#5 0x0000555555589608 in boost::beast::basic_multi_buffer<std::allocator<char>
::subrange<true>::const_iterator::operator* (this=0x7fffffffc4f8) at ../boost/boost/beast/core/impl/multi_buffer.hpp:384
#6 0x0000555555589b4b in boost::asio::buffers_iterator<boost::beast::basic_multi_buffer<std::allocator<char>
::subrange<true>, char>::increment (this=0x7fffffffc4d0)
at ../boost/boost/asio/buffers_iterator.hpp:370
#7 0x0000555555584894 in boost::asio::buffers_iterator<boost::beast::basic_multi_buffer<std::allocator<char>
::subrange<true>, char>::operator++ (this=0x7fffffffc4d0)
at ../boost/boost/asio/buffers_iterator.hpp:229
#8 0x000055555557e7c0 in boost::asio::buffers_iterator<boost::beast::basic_multi_buffer<std::allocator<char>
::subrange<true>, char>::operator++ (this=0x7fffffffc4d0)
at ../boost/boost/asio/buffers_iterator.hpp:237
#9 0x0000555555579ba8 in handle_request<boost::beast::http::basic_dynamic_body<boost::beast::basic_multi_buffer<std::allocator<char>
, std::allocator<char>, session::send_lambda&> (doc_root=...,
req=..., send=...) at t.cpp:164
I hope anybody can give my a hint on how to use this beast.
Right now it feels that it might be easier to write the http server using plain boost::asio.
*Klebsch Mario *Funktion | R&D
Tel: +49 (0) 531 38 701 718 Raum: Braunschweig, E20
Diese E-Mail und die an sie angehängten Dateien sind ausschließlich für Personen oder Institutionen bestimmt, deren Namen oben aufgeführt sind. Sie können Informationen enthalten, die durch das Berufsgeheimnis geschützt sind und deren Weitergabe strengstens untersagt ist. Jede elektronische Nachricht kann geändert werden. ACTIA lehnt jede Verantwortung für diese Nachricht ab. Der Inhalt dieser Nachricht stellt keine Verpflichtung seitens unseres Unternehmens dar. Wenn Sie kein Empfänger sind, weisen wir Sie darauf hin, dass das Lesen, Vervielfältigen oder Verteilen strengstens untersagt ist. Wir bitten Sie daher, uns umgehend über diesen Brief zu informieren und diese Nachricht sowie eventuell beigefügte Unterlagen aus Ihrem Postfach zu löschen. Danke.
This e-mail and the files attached to it are intended exclusively for persons or institutions whose names are listed above. They may contain information that is protected by professional secrecy and the disclosure of which is strictly prohibited. Any electronic message can be modified. ACTIA declines all responsibility for this message. The content of this message does not represent a commitment on the part of our company. If you are not a recipient, we would like to point out that reading, copying or distribution is strictly prohibited. We therefore ask you to inform us immediately about this letter and to delete this message and any accompanying documents from your mailbox. Thank you.
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org https://lists.boost.org/mailman/listinfo.cgi/boost-users
Hello,
Would you mind sharing your code (perhaps in a git repo) so that I can reproduce, debug and advise?
No problem, the code is just a slightly modified version of the example: // // 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 server, asynchronous // //------------------------------------------------------------------------------ #include <boost/beast/core.hpp> #include <boost/beast/http.hpp> #include <boost/beast/version.hpp> #include <boost/asio/dispatch.hpp> #include <boost/asio/strand.hpp> #include <boost/asio/buffers_iterator.hpp> #include <boost/config.hpp> #include <algorithm> #include <cstdlib> #include <functional> #include <iostream> #include <memory> #include <string> #include <thread> #include <vector> namespace beast = boost::beast; // from <boost/beast.hpp> namespace http = beast::http; // from <boost/beast/http.hpp> namespace net = boost::asio; // from <boost/asio.hpp> using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> // Return a reasonable mime type based on the extension of a file. beast::string_view mime_type(beast::string_view path) { using beast::iequals; auto const ext = [&path] { auto const pos = path.rfind("."); if(pos == beast::string_view::npos) return beast::string_view{}; return path.substr(pos); }(); if(iequals(ext, ".htm")) return "text/html"; if(iequals(ext, ".html")) return "text/html"; if(iequals(ext, ".php")) return "text/html"; if(iequals(ext, ".css")) return "text/css"; if(iequals(ext, ".txt")) return "text/plain"; if(iequals(ext, ".js")) return "application/javascript"; if(iequals(ext, ".json")) return "application/json"; if(iequals(ext, ".xml")) return "application/xml"; if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; if(iequals(ext, ".flv")) return "video/x-flv"; if(iequals(ext, ".png")) return "image/png"; if(iequals(ext, ".jpe")) return "image/jpeg"; if(iequals(ext, ".jpeg")) return "image/jpeg"; if(iequals(ext, ".jpg")) return "image/jpeg"; if(iequals(ext, ".gif")) return "image/gif"; if(iequals(ext, ".bmp")) return "image/bmp"; if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; if(iequals(ext, ".tiff")) return "image/tiff"; if(iequals(ext, ".tif")) return "image/tiff"; if(iequals(ext, ".svg")) return "image/svg+xml"; if(iequals(ext, ".svgz")) return "image/svg+xml"; return "application/text"; } // Append an HTTP rel-path to a local filesystem path. // The returned path is normalized for the platform. std::string path_cat( beast::string_view base, beast::string_view path) { if(base.empty()) return std::string(path); std::string result(base); #ifdef BOOST_MSVC char constexpr path_separator = '\\'; if(result.back() == path_separator) result.resize(result.size() - 1); result.append(path.data(), path.size()); for(auto& c : result) if(c == '/') c = path_separator; #else char constexpr path_separator = '/'; if(result.back() == path_separator) result.resize(result.size() - 1); result.append(path.data(), path.size()); #endif return result; } // This function produces an HTTP response for the given // request. The type of the response object depends on the // contents of the request, so the interface requires the // caller to pass a generic lambda for receiving the response. template< class Body, class Allocator, class Send> void handle_request( beast::string_view doc_root, http::request<Body, http::basic_fields<Allocator>>&& req, Send&& send) { // Returns a bad request response auto const bad_request = [&req](beast::string_view why) { http::response<http::string_body> res{http::status::bad_request, req.version()}; res.set(http::field::server, BOOST_BEAST_VERSION_STRING); res.set(http::field::content_type, "text/html"); res.keep_alive(req.keep_alive()); res.body() = std::string(why); res.prepare_payload(); return res; }; // Returns a not found response auto const not_found = [&req](beast::string_view target) { http::response<http::string_body> res{http::status::not_found, req.version()}; res.set(http::field::server, BOOST_BEAST_VERSION_STRING); res.set(http::field::content_type, "text/html"); res.keep_alive(req.keep_alive()); res.body() = "The resource '" + std::string(target) + "' was not found."; res.prepare_payload(); return res; }; // Returns a server error response auto const server_error = [&req](beast::string_view what) { http::response<http::string_body> res{http::status::internal_server_error, req.version()}; res.set(http::field::server, BOOST_BEAST_VERSION_STRING); res.set(http::field::content_type, "text/html"); res.keep_alive(req.keep_alive()); res.body() = "An error occurred: '" + std::string(what) + "'"; res.prepare_payload(); return res; }; if (req.method() == http::verb::post && req.target()=="/xxx" ) { for (const auto &h:req) printf("heder: %s=%s\n", std::string(h.name_string()).c_str(), std::string(h.value()).c_str()); printf("body as string:\n"); std::string body{ boost::asio::buffers_begin(req.body().data()), boost::asio::buffers_end(req.body().data()) }; printf("%s", body.c_str()); printf("end of body as string\n"); printf("body with iterator:\n"); const auto begin = boost::asio::buffers_begin(req.body().data()); const auto end = boost::asio::buffers_end(req.body().data()); unsigned x=0; for (auto it = begin; it != end; it++) putchar(*it); printf("end of body with iterator\n"); } // Make sure we can handle the method if( req.method() != http::verb::get && req.method() != http::verb::head) return send(bad_request("Unknown HTTP-method")); // Request path must be absolute and not contain "..". if( req.target().empty() || req.target()[0] != '/' || req.target().find("..") != beast::string_view::npos) return send(bad_request("Illegal request-target")); // Build the path to the requested file std::string path = path_cat(doc_root, req.target()); if(req.target().back() == '/') path.append("index.html"); // Attempt to open the file beast::error_code ec; http::file_body::value_type body; body.open(path.c_str(), beast::file_mode::scan, ec); // Handle the case where the file doesn't exist if(ec == beast::errc::no_such_file_or_directory) return send(not_found(req.target())); // Handle an unknown error if(ec) return send(server_error(ec.message())); // Cache the size since we need it after the move auto const size = body.size(); // Respond to HEAD request if(req.method() == http::verb::head) { http::response<http::empty_body> res{http::status::ok, req.version()}; res.set(http::field::server, BOOST_BEAST_VERSION_STRING); res.set(http::field::content_type, mime_type(path)); res.content_length(size); res.keep_alive(req.keep_alive()); return send(std::move(res)); } // Respond to GET request http::response<http::file_body> res{ std::piecewise_construct, std::make_tuple(std::move(body)), std::make_tuple(http::status::ok, req.version())}; res.set(http::field::server, BOOST_BEAST_VERSION_STRING); res.set(http::field::content_type, mime_type(path)); res.content_length(size); res.keep_alive(req.keep_alive()); return send(std::move(res)); } //------------------------------------------------------------------------------ // Report a failure void fail(beast::error_code ec, char const* what) { std::cerr << what << ": " << ec.message() << "\n"; } // Handles an HTTP server connection class session : public std::enable_shared_from_this<session> { // This is the C++11 equivalent of a generic lambda. // The function object is used to send an HTTP message. struct send_lambda { session& self_; explicit send_lambda(session& self) : self_(self) { } template<bool isRequest, class Body, class Fields> void operator()(http::message<isRequest, Body, Fields>&& msg) const { // The lifetime of the message has to extend // for the duration of the async operation so // we use a shared_ptr to manage it. auto sp = std::make_shared< http::message<isRequest, Body, Fields>>(std::move(msg)); // Store a type-erased version of the shared // pointer in the class to keep it alive. self_.res_ = sp; // Write the response http::async_write( self_.stream_, *sp, beast::bind_front_handler( &session::on_write, self_.shared_from_this(), sp->need_eof())); } }; beast::tcp_stream stream_; beast::flat_buffer buffer_; std::shared_ptr<std::string const> doc_root_; // http::request<http::string_body> req_; http::request<http::dynamic_body> req_; std::shared_ptr<void> res_; send_lambda lambda_; public: // Take ownership of the stream session( tcp::socket&& socket, std::shared_ptr<std::string const> const& doc_root) : stream_(std::move(socket)) , doc_root_(doc_root) , lambda_(*this) { } // Start the asynchronous operation void run() { // We need to be executing within a strand to perform async operations // on the I/O objects in this session. Although not strictly necessary // for single-threaded contexts, this example code is written to be // thread-safe by default. net::dispatch(stream_.get_executor(), beast::bind_front_handler( &session::do_read, shared_from_this())); } void do_read() { // Make the request empty before reading, // otherwise the operation behavior is undefined. req_ = {}; // Set the timeout. stream_.expires_after(std::chrono::seconds(30)); // Read a request http::async_read(stream_, buffer_, req_, beast::bind_front_handler( &session::on_read, shared_from_this())); } void on_read( beast::error_code ec, std::size_t bytes_transferred) { boost::ignore_unused(bytes_transferred); // This means they closed the connection if(ec == http::error::end_of_stream) return do_close(); if(ec) return fail(ec, "read"); // Send the response handle_request(*doc_root_, std::move(req_), lambda_); } void on_write( bool close, beast::error_code ec, std::size_t bytes_transferred) { boost::ignore_unused(bytes_transferred); if(ec) return fail(ec, "write"); if(close) { // This means we should close the connection, usually because // the response indicated the "Connection: close" semantic. return do_close(); } // We're done with the response so delete it res_ = nullptr; // Read another request do_read(); } void do_close() { // Send a TCP shutdown beast::error_code ec; stream_.socket().shutdown(tcp::socket::shutdown_send, ec); // At this point the connection is closed gracefully } }; //------------------------------------------------------------------------------ // Accepts incoming connections and launches the sessions class listener : public std::enable_shared_from_this<listener> { net::io_context& ioc_; tcp::acceptor acceptor_; std::shared_ptr<std::string const> doc_root_; public: listener( net::io_context& ioc, tcp::endpoint endpoint, std::shared_ptr<std::string const> const& doc_root) : ioc_(ioc) , acceptor_(net::make_strand(ioc)) , doc_root_(doc_root) { beast::error_code ec; // Open the acceptor acceptor_.open(endpoint.protocol(), ec); if(ec) { fail(ec, "open"); return; } // Allow address reuse acceptor_.set_option(net::socket_base::reuse_address(true), ec); if(ec) { fail(ec, "set_option"); return; } // Bind to the server address acceptor_.bind(endpoint, ec); if(ec) { fail(ec, "bind"); return; } // Start listening for connections acceptor_.listen( net::socket_base::max_listen_connections, ec); if(ec) { fail(ec, "listen"); return; } } // Start accepting incoming connections void run() { do_accept(); } private: void do_accept() { // The new connection gets its own strand acceptor_.async_accept( net::make_strand(ioc_), beast::bind_front_handler( &listener::on_accept, shared_from_this())); } void on_accept(beast::error_code ec, tcp::socket socket) { if(ec) { fail(ec, "accept"); } else { // Create the session and run it std::make_shared<session>( std::move(socket), doc_root_)->run(); } // Accept another connection do_accept(); } }; //------------------------------------------------------------------------------ int main(int argc, char* argv[]) { // Check command line arguments. if (argc != 4) { std::cerr << "Usage: http-server-async <address> <port> <doc_root>\n" << "Example:\n" << " http-server-async 0.0.0.0 8080 .\n"; return EXIT_FAILURE; } auto const address = net::ip::make_address(argv[1]); auto const port = static_cast<unsigned short>(std::atoi(argv[2])); auto const doc_root = std::make_shared<std::string>(argv[3]); // The io_context is required for all I/O net::io_context ioc; // Create and launch a listening port std::make_shared<listener>( ioc, tcp::endpoint{address, port}, doc_root)->run(); // Run the I/O service printf("running\n"); ioc.run(); return EXIT_SUCCESS; } And this is my index.html: <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd> <html xmlns=http://www.w3.org/1999/xhtml> <body> <form action="xxx" method="post" accept-charset="utf-8" enctype="multipart/form-data"> <label for="field0">field 0:</label><input type="text" id="field0" name="field0"/><br/><br/> <label for="field1">field 1:</label><input type="password" id="field1" name="field1"/><br/><br/> <label for="field2">field 2:</label><input type="text" id="field2" name="field2"/><br/><br/> <label for="field3">field 3:</label><input type="text" id="field3" name="field3"/><br/><br/> <label for="field4">field 4:</label><input type="file" id="field4" name="field4"/><br/><br/> <input type="submit" value="Submit"/> </form> </body> </html> Klebsch Mario Funktion | R&D Tel: +49 (0) 531 38 701 718 Raum: Braunschweig, E20 Diese E-Mail und die an sie angehängten Dateien sind ausschließlich für Personen oder Institutionen bestimmt, deren Namen oben aufgeführt sind. Sie können Informationen enthalten, die durch das Berufsgeheimnis geschützt sind und deren Weitergabe strengstens untersagt ist. Jede elektronische Nachricht kann geändert werden. ACTIA lehnt jede Verantwortung für diese Nachricht ab. Der Inhalt dieser Nachricht stellt keine Verpflichtung seitens unseres Unternehmens dar. Wenn Sie kein Empfänger sind, weisen wir Sie darauf hin, dass das Lesen, Vervielfältigen oder Verteilen strengstens untersagt ist. Wir bitten Sie daher, uns umgehend über diesen Brief zu informieren und diese Nachricht sowie eventuell beigefügte Unterlagen aus Ihrem Postfach zu löschen. Danke. This e-mail and the files attached to it are intended exclusively for persons or institutions whose names are listed above. They may contain information that is protected by professional secrecy and the disclosure of which is strictly prohibited. Any electronic message can be modified. ACTIA declines all responsibility for this message. The content of this message does not represent a commitment on the part of our company. If you are not a recipient, we would like to point out that reading, copying or distribution is strictly prohibited. We therefore ask you to inform us immediately about this letter and to delete this message and any accompanying documents from your mailbox. Thank you.
participants (2)
-
Klebsch, Mario
-
Richard Hodges