mirror of
https://github.com/boostorg/beast.git
synced 2025-08-03 23:04:35 +02:00
Add http-server-small example
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
Version 67:
|
Version 67:
|
||||||
|
|
||||||
* Fix doc example link
|
* Fix doc example link
|
||||||
|
* Add http-server-small example
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@@ -66,7 +66,7 @@ over a TLS connection. Requires OpenSSL to build.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
[section HTTP Server]
|
[section HTTP Server (Fast)]
|
||||||
|
|
||||||
This example implements a very simple HTTP server with
|
This example implements a very simple HTTP server with
|
||||||
some optimizations suitable for calculating benchmarks.
|
some optimizations suitable for calculating benchmarks.
|
||||||
@@ -78,6 +78,17 @@ some optimizations suitable for calculating benchmarks.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[section HTTP Server (Small)]
|
||||||
|
|
||||||
|
This example implements a very simple HTTP server
|
||||||
|
suitable as a starting point on an embedded device.
|
||||||
|
|
||||||
|
* [repo_file example/http-server-small/http_server_small.cpp]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[section WebSocket Client (with SSL)]
|
[section WebSocket Client (with SSL)]
|
||||||
|
|
||||||
Establish a WebSocket connection over an encrypted TLS connection,
|
Establish a WebSocket connection over an encrypted TLS connection,
|
||||||
|
@@ -4,6 +4,7 @@ add_subdirectory (echo-op)
|
|||||||
add_subdirectory (http-client)
|
add_subdirectory (http-client)
|
||||||
add_subdirectory (http-crawl)
|
add_subdirectory (http-crawl)
|
||||||
add_subdirectory (http-server)
|
add_subdirectory (http-server)
|
||||||
|
add_subdirectory (http-server-small)
|
||||||
add_subdirectory (server-framework)
|
add_subdirectory (server-framework)
|
||||||
add_subdirectory (websocket-client)
|
add_subdirectory (websocket-client)
|
||||||
|
|
||||||
|
15
example/http-server-small/CMakeLists.txt
Normal file
15
example/http-server-small/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Part of Beast
|
||||||
|
|
||||||
|
GroupSources(include/beast beast)
|
||||||
|
|
||||||
|
GroupSources(example/http-server-small "/")
|
||||||
|
|
||||||
|
add_executable (http-server-small
|
||||||
|
${BEAST_INCLUDES}
|
||||||
|
http_server_small.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(http-server-small
|
||||||
|
Beast
|
||||||
|
)
|
||||||
|
|
13
example/http-server-small/Jamfile
Normal file
13
example/http-server-small/Jamfile
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2013-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)
|
||||||
|
#
|
||||||
|
|
||||||
|
exe http-server-small :
|
||||||
|
http_server_small.cpp
|
||||||
|
:
|
||||||
|
<variant>coverage:<build>no
|
||||||
|
<variant>ubasan:<build>no
|
||||||
|
;
|
240
example/http-server-small/http_server_small.cpp
Normal file
240
example/http-server-small/http_server_small.cpp
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2017 Christopher M. Kohlhoff (chris at kohlhoff 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)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <beast/core.hpp>
|
||||||
|
#include <beast/http.hpp>
|
||||||
|
#include <beast/version.hpp>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <ctime>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace ip = boost::asio::ip; // from <boost/asio.hpp>
|
||||||
|
using tcp = boost::asio::ip::tcp; // from <boost/asio.hpp>
|
||||||
|
namespace http = beast::http; // from <beast/http.hpp>
|
||||||
|
|
||||||
|
namespace my_program_state
|
||||||
|
{
|
||||||
|
std::size_t
|
||||||
|
request_count()
|
||||||
|
{
|
||||||
|
static std::size_t count = 0;
|
||||||
|
return ++count;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::time_t
|
||||||
|
now()
|
||||||
|
{
|
||||||
|
return std::time(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class http_connection : public std::enable_shared_from_this<http_connection>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
http_connection(tcp::socket socket)
|
||||||
|
: socket_(std::move(socket))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initiate the asynchronous operations associated with the connection.
|
||||||
|
void
|
||||||
|
start()
|
||||||
|
{
|
||||||
|
read_request();
|
||||||
|
check_deadline();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// The socket for the currently connected client.
|
||||||
|
tcp::socket socket_;
|
||||||
|
|
||||||
|
// The buffer for performing reads.
|
||||||
|
beast::flat_buffer buffer_{8192};
|
||||||
|
|
||||||
|
// The request message.
|
||||||
|
http::request<http::dynamic_body> request_;
|
||||||
|
|
||||||
|
// The response message.
|
||||||
|
http::response<http::dynamic_body> response_;
|
||||||
|
|
||||||
|
// The timer for putting a deadline on connection processing.
|
||||||
|
boost::asio::basic_waitable_timer<std::chrono::steady_clock> deadline_{
|
||||||
|
socket_.get_io_service(), std::chrono::seconds(60)};
|
||||||
|
|
||||||
|
// Asynchronously receive a complete request message.
|
||||||
|
void
|
||||||
|
read_request()
|
||||||
|
{
|
||||||
|
auto self{shared_from_this()};
|
||||||
|
|
||||||
|
http::async_read(
|
||||||
|
socket_,
|
||||||
|
buffer_,
|
||||||
|
request_,
|
||||||
|
[self](beast::error_code ec)
|
||||||
|
{
|
||||||
|
if(!ec)
|
||||||
|
self->process_request();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine what needs to be done with the request message.
|
||||||
|
void
|
||||||
|
process_request()
|
||||||
|
{
|
||||||
|
response_.version = 11;
|
||||||
|
response_.set(http::field::connection, "close");
|
||||||
|
|
||||||
|
switch(request_.method())
|
||||||
|
{
|
||||||
|
case http::verb::get:
|
||||||
|
response_.result(http::status::ok);
|
||||||
|
response_.set(http::field::server, "Beast");
|
||||||
|
create_response();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// We return responses indicating an error if
|
||||||
|
// we do not recognize the request method.
|
||||||
|
response_.result(http::status::bad_request);
|
||||||
|
response_.set(http::field::content_type, "text/plain");
|
||||||
|
beast::ostream(response_.body)
|
||||||
|
<< "Invalid request-method '"
|
||||||
|
<< request_.method_string().to_string()
|
||||||
|
<< "'";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_response();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct a response message based on the program state.
|
||||||
|
void
|
||||||
|
create_response()
|
||||||
|
{
|
||||||
|
if(request_.target() == "/count")
|
||||||
|
{
|
||||||
|
response_.set(http::field::content_type, "text/html");
|
||||||
|
beast::ostream(response_.body)
|
||||||
|
<< "<html>\n"
|
||||||
|
<< "<head><title>Request count</title></head>\n"
|
||||||
|
<< "<body>\n"
|
||||||
|
<< "<h1>Request count</h1>\n"
|
||||||
|
<< "<p>There have been "
|
||||||
|
<< my_program_state::request_count()
|
||||||
|
<< " requests so far.</p>\n"
|
||||||
|
<< "</body>\n"
|
||||||
|
<< "</html>\n";
|
||||||
|
}
|
||||||
|
else if(request_.target() == "/time")
|
||||||
|
{
|
||||||
|
response_.set(http::field::content_type, "text/html");
|
||||||
|
beast::ostream(response_.body)
|
||||||
|
<< "<html>\n"
|
||||||
|
<< "<head><title>Current time</title></head>\n"
|
||||||
|
<< "<body>\n"
|
||||||
|
<< "<h1>Current time</h1>\n"
|
||||||
|
<< "<p>The current time is "
|
||||||
|
<< my_program_state::now()
|
||||||
|
<< " seconds since the epoch.</p>\n"
|
||||||
|
<< "</body>\n"
|
||||||
|
<< "</html>\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response_.result(http::status::not_found);
|
||||||
|
response_.set(http::field::content_type, "text/plain");
|
||||||
|
beast::ostream(response_.body) << "File not found\r\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asynchronously transmit the response message.
|
||||||
|
void
|
||||||
|
write_response()
|
||||||
|
{
|
||||||
|
auto self{shared_from_this()};
|
||||||
|
|
||||||
|
response_.set(http::field::content_length, response_.body.size());
|
||||||
|
|
||||||
|
http::async_write(
|
||||||
|
socket_,
|
||||||
|
response_,
|
||||||
|
[self](beast::error_code ec)
|
||||||
|
{
|
||||||
|
self->socket_.shutdown(tcp::socket::shutdown_send, ec);
|
||||||
|
self->deadline_.cancel();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether we have spent enough time on this connection.
|
||||||
|
void
|
||||||
|
check_deadline()
|
||||||
|
{
|
||||||
|
auto self{shared_from_this()};
|
||||||
|
|
||||||
|
deadline_.async_wait(
|
||||||
|
[self](beast::error_code ec)
|
||||||
|
{
|
||||||
|
if(!ec)
|
||||||
|
{
|
||||||
|
// Close socket to cancel any outstanding operation.
|
||||||
|
self->socket_.close(ec);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// "Loop" forever accepting new connections.
|
||||||
|
void
|
||||||
|
http_server(tcp::acceptor& acceptor, tcp::socket& socket)
|
||||||
|
{
|
||||||
|
acceptor.async_accept(socket,
|
||||||
|
[&](beast::error_code ec)
|
||||||
|
{
|
||||||
|
if(!ec)
|
||||||
|
std::make_shared<http_connection>(std::move(socket))->start();
|
||||||
|
http_server(acceptor, socket);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Check command line arguments.
|
||||||
|
if(argc != 3)
|
||||||
|
{
|
||||||
|
std::cerr << "Usage: " << argv[0] << " <address> <port>\n";
|
||||||
|
std::cerr << " For IPv4, try:\n";
|
||||||
|
std::cerr << " receiver 0.0.0.0 80\n";
|
||||||
|
std::cerr << " For IPv6, try:\n";
|
||||||
|
std::cerr << " receiver 0::0 80\n";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto address = ip::address::from_string(argv[1]);
|
||||||
|
unsigned short port = static_cast<unsigned short>(std::atoi(argv[2]));
|
||||||
|
|
||||||
|
boost::asio::io_service ios{1};
|
||||||
|
|
||||||
|
tcp::acceptor acceptor{ios, {address, port}};
|
||||||
|
tcp::socket socket{ios};
|
||||||
|
http_server(acceptor, socket);
|
||||||
|
|
||||||
|
ios.run();
|
||||||
|
}
|
||||||
|
catch(std::exception const& e)
|
||||||
|
{
|
||||||
|
std::cerr << "Exception: " << e.what() << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// Copyright (c) 2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
//
|
//
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
// 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)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
Reference in New Issue
Block a user