1//
2// Copyright (c) 2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
3//
4// Distributed under the Boost Software License, Version 1.0. (See accompanying
5// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6//
7// Official repository: https://github.com/boostorg/beast
8//
9
10//------------------------------------------------------------------------------
11//
12// Example: HTTP server, small
13//
14//------------------------------------------------------------------------------
15
16#include <boost/beast/core.hpp>
17#include <boost/beast/http.hpp>
18#include <boost/beast/version.hpp>
19#include <boost/asio.hpp>
20#include <chrono>
21#include <cstdlib>
22#include <ctime>
23#include <iostream>
24#include <memory>
25#include <string>
26
27namespace beast = boost::beast; // from <boost/beast.hpp>
28namespace http = beast::http; // from <boost/beast/http.hpp>
29namespace net = boost::asio; // from <boost/asio.hpp>
30using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
31
32namespace my_program_state
33{
34 std::size_t
35 request_count()
36 {
37 static std::size_t count = 0;
38 return ++count;
39 }
40
41 std::time_t
42 now()
43 {
44 return std::time(timer: 0);
45 }
46}
47
48class http_connection : public std::enable_shared_from_this<http_connection>
49{
50public:
51 http_connection(tcp::socket socket)
52 : socket_(std::move(socket))
53 {
54 }
55
56 // Initiate the asynchronous operations associated with the connection.
57 void
58 start()
59 {
60 read_request();
61 check_deadline();
62 }
63
64private:
65 // The socket for the currently connected client.
66 tcp::socket socket_;
67
68 // The buffer for performing reads.
69 beast::flat_buffer buffer_{8192};
70
71 // The request message.
72 http::request<http::dynamic_body> request_;
73
74 // The response message.
75 http::response<http::dynamic_body> response_;
76
77 // The timer for putting a deadline on connection processing.
78 net::steady_timer deadline_{
79 socket_.get_executor(), std::chrono::seconds(60)};
80
81 // Asynchronously receive a complete request message.
82 void
83 read_request()
84 {
85 auto self = shared_from_this();
86
87 http::async_read(
88 stream&: socket_,
89 buffer&: buffer_,
90 msg&: request_,
91 handler: [self](beast::error_code ec,
92 std::size_t bytes_transferred)
93 {
94 boost::ignore_unused(bytes_transferred);
95 if(!ec)
96 self->process_request();
97 });
98 }
99
100 // Determine what needs to be done with the request message.
101 void
102 process_request()
103 {
104 response_.version(value: request_.version());
105 response_.keep_alive(value: false);
106
107 switch(request_.method())
108 {
109 case http::verb::get:
110 response_.result(v: http::status::ok);
111 response_.set(name: http::field::server, value: "Beast");
112 create_response();
113 break;
114
115 default:
116 // We return responses indicating an error if
117 // we do not recognize the request method.
118 response_.result(v: http::status::bad_request);
119 response_.set(name: http::field::content_type, value: "text/plain");
120 beast::ostream(buffer&: response_.body())
121 << "Invalid request-method '"
122 << std::string(request_.method_string())
123 << "'";
124 break;
125 }
126
127 write_response();
128 }
129
130 // Construct a response message based on the program state.
131 void
132 create_response()
133 {
134 if(request_.target() == "/count")
135 {
136 response_.set(name: http::field::content_type, value: "text/html");
137 beast::ostream(buffer&: response_.body())
138 << "<html>\n"
139 << "<head><title>Request count</title></head>\n"
140 << "<body>\n"
141 << "<h1>Request count</h1>\n"
142 << "<p>There have been "
143 << my_program_state::request_count()
144 << " requests so far.</p>\n"
145 << "</body>\n"
146 << "</html>\n";
147 }
148 else if(request_.target() == "/time")
149 {
150 response_.set(name: http::field::content_type, value: "text/html");
151 beast::ostream(buffer&: response_.body())
152 << "<html>\n"
153 << "<head><title>Current time</title></head>\n"
154 << "<body>\n"
155 << "<h1>Current time</h1>\n"
156 << "<p>The current time is "
157 << my_program_state::now()
158 << " seconds since the epoch.</p>\n"
159 << "</body>\n"
160 << "</html>\n";
161 }
162 else
163 {
164 response_.result(v: http::status::not_found);
165 response_.set(name: http::field::content_type, value: "text/plain");
166 beast::ostream(buffer&: response_.body()) << "File not found\r\n";
167 }
168 }
169
170 // Asynchronously transmit the response message.
171 void
172 write_response()
173 {
174 auto self = shared_from_this();
175
176 response_.content_length(value: response_.body().size());
177
178 http::async_write(
179 stream&: socket_,
180 msg: response_,
181 handler: [self](beast::error_code ec, std::size_t)
182 {
183 self->socket_.shutdown(what: tcp::socket::shutdown_send, ec);
184 self->deadline_.cancel();
185 });
186 }
187
188 // Check whether we have spent enough time on this connection.
189 void
190 check_deadline()
191 {
192 auto self = shared_from_this();
193
194 deadline_.async_wait(
195 token: [self](beast::error_code ec)
196 {
197 if(!ec)
198 {
199 // Close socket to cancel any outstanding operation.
200 self->socket_.close(ec);
201 }
202 });
203 }
204};
205
206// "Loop" forever accepting new connections.
207void
208http_server(tcp::acceptor& acceptor, tcp::socket& socket)
209{
210 acceptor.async_accept(peer&: socket,
211 token: [&](beast::error_code ec)
212 {
213 if(!ec)
214 std::make_shared<http_connection>(args: std::move(socket))->start();
215 http_server(acceptor, socket);
216 });
217}
218
219int
220main(int argc, char* argv[])
221{
222 try
223 {
224 // Check command line arguments.
225 if(argc != 3)
226 {
227 std::cerr << "Usage: " << argv[0] << " <address> <port>\n";
228 std::cerr << " For IPv4, try:\n";
229 std::cerr << " receiver 0.0.0.0 80\n";
230 std::cerr << " For IPv6, try:\n";
231 std::cerr << " receiver 0::0 80\n";
232 return EXIT_FAILURE;
233 }
234
235 auto const address = net::ip::make_address(str: argv[1]);
236 unsigned short port = static_cast<unsigned short>(std::atoi(nptr: argv[2]));
237
238 net::io_context ioc{1};
239
240 tcp::acceptor acceptor{ioc, {address, port}};
241 tcp::socket socket{ioc};
242 http_server(acceptor, socket);
243
244 ioc.run();
245 }
246 catch(std::exception const& e)
247 {
248 std::cerr << "Error: " << e.what() << std::endl;
249 return EXIT_FAILURE;
250 }
251}
252

source code of boost/libs/beast/example/http/server/small/http_server_small.cpp