An application creates an HTTP server type by instantiating the http_server
class template defined in <via/http_server.hpp>
:
namespace via
{
template <typename SocketAdaptor, typename Container = std::vector<char>, bool use_strand = false>
class http_server
{
...
}
}
The http_server
class template parameters are:
Parameter | Default | Description |
---|---|---|
SocketAdaptor | via::comms::tcp_adaptor for HTTP orvia::comms::ssl::ssl_tcp_adaptor for HTTPS. |
|
Container | std::vector<char> |
std::vector<char> for data orstd::string for text |
use_strand | false | Use an asio::strand to manage multiple threads,see: boost asio strands |
E.g. an HTTP server using std::string as a Container:
#include "via/comms/tcp_adaptor.hpp"
#include "via/http_server.hpp"
typedef via::http_server<via::comms::tcp_adaptor, std::string> http_server_type;
/// A typedef for http_connection_type to make following code easier to understand.
typedef http_server_type::http_connection_type http_connection;
The server is constructed with an asio::io_service
, e.g.:
boost::asio::io_service io_service;
http_server_type http_server(io_service);
The can either be configured with handlers for each specific method/uri combination, see Request Routing, e.g.:
http_server.request_router().add_method("GET", "/hello", get_hello_handler);
Or with a RequestHandler
, see Server Events, e.g.:
https_server.request_received_event(request_handler);
In which case the request_handler
must route the HTTP requests.
The server will call an application's event handler (callback function)
whenever a significant event occurs.
See Server Events for more details.
Event | Function to Register Callback | Description |
---|---|---|
Request Received | request_received_event | A valid HTTP request has been received. |
Chunk Received | chunk_received_event | A valid HTTP chunk has been received. |
Expect Continue | request_expect_continue_event | A valid HTTP request has been received with an "Expect: 100-continue" header. |
Invalid Request | invalid_request_event | An invalid HTTP request has been received. |
Socket Connected | socket_connected_event | A socket has connected. |
Socket Disconnected | socket_disconnected_event | A socket has disconnected. |
Message Sent | message_sent_event | A message has been sent on the connection. |
Note: if an event handler is provided for Request Received then the
internal request_router()
is disabled.
The application can then configure the server parameters.
See Server Configuration for more details.
Some of the more significant parameters (with their default values) are:
Parameter | Default | Description |
---|---|---|
max_method_length | 8 | The maximum length of a request method. |
max_uri_length | 1024 | The maximum length of a request URI. |
max_header_number | 100 | The maximum number of header fields in a request. |
max_header_length | 8190 | The maximum length of characters in the headers. |
max_body_size | 1Mb | The maximum size of a request body. |
max_chunk_size | 1Mb | The maximum size of each request chunk. |
Note: only valid for servers using via::comms::ssl::ssl_tcp_adaptor
as a template parameter.
Parameter | Description |
---|---|
set_password | Sets the SSL/TLS password. |
set_ssl_files | Sets the SSL/TLS files: certificate_file, key file and dh_file. |
E.g:
// Set up SSL
https_server.set_password(password);
boost::system::error_code error
(https_server_type::set_ssl_files(certificate_file, private_key_file));
if (error)
{
std::cerr << "Error, set_ssl_files: " << error.message() << std::endl;
return 1;
}
Other SSL/TLS options can be set via the ssl_context, e.g.:
boost::asio::ssl::context& ssl_context
(https_server_type::connection_type::ssl_context());
ssl_context.set_options(boost::asio::ssl::context_base::default_workarounds);
See: asio ssl context base for options.
Once an http_server
has been created and configured, it can accept connections
on the required port by calling accept_connections
, e.g.:
boost::system::error_code error(http_server.accept_connections());
if (error)
{
std::cerr << "Error: " << error.message() << std::endl;
return 1;
}
The default parameters for accept_connections
are the default port
for the server type (80 for HTTP, 443 for HTTPS) and IPV6 and IPV4
connections. The parameters are:
Parameter | Default | Description |
---|---|---|
port | 80 or 443 | The server port number. Default 80 for HTTP, 443 for HTTPS. |
ipv4_only | false | If set, it only accepts connections from IPV4 clients. Default accepts IPV6 and IPV4. |
The function returns a boost::system::error_code
to determine whether the server was
able to open the TCP acceptor successfully. If unsuccessful, error.message()
may be called to determine the type of error as shown in the example above.
The server's communication is handled by the boost asio
library. For the server
to start accepting connections, run
must be called on theasio::io_service
to
run the server, see: boost libs: Asio.
// run the io_service to start communications
io_service.run();
The server will call request_handler
whenever it receives a valid HTTP request from a client.
Note: the call to io_service.run()
will not return until the server is closed.
The application creates an HTTP response in a via::http::tx_response
class,
either by just passing it the response status code, and adding HTTP headers e.g:
via::http::tx_response response(via::http::response_status::code::OK);
response.add_server_header();
response.add_date_header();
Or the application can create a response with a status code and a string of HTTP headers, e.g.:
std::string header_string("Allow: GET, HEAD, PUT\r\n");
via::http::tx_response response(via::http::response_status::code::OK, header_string);
However, the application must ensure that the header_string does not contain
any "split headers" (see: Security Guide) before sending it.
Since the server will not send an HTTP response containing "split headers".
The tx_response::is_valid()
can be used to test for split headers, e.g.:
if (response.is_valid())
... // OK
else
... // response contains split headers
The response is then sent by calling one of the http_connection
's send functions, e.g.:
weak_ptr.lock()->send(response);
Note: since the pointer passed to the request handler is a weak pointer, lock()
must be called to convert it into a shared pointer
so that send
can be called.
The server has an number of different send
functions that the application may call:
Function | Data | Description |
---|---|---|
send_response() | Send the standard response to an invalid request. Only to be used in the invalid request handler. |
|
send(response) | Send an HTTP response without a body. |
|
send(response, body) | Container | Send a response with body , data buffered by http_connection . |
send(response, buffers) | ConstBuffers | Send a response with body , data unbuffered. |
send_chunk(data) | Container | Send response chunk data, buffered by http_connection . |
send_chunk(buffers, buffers) | ConstBuffers | Send response chunk data, unbuffered. |
last_chunk() | Send response HTTP last chunk . |
All of the functions send the data asynchronously, i.e. they return before the data is sent. The application can choose between two types of functions depending upon whether the data that it is sending is temporary or not:
-
buffered functions, i.e.: those taking a copy of Container as a parameter.
These functions take a copy of the data so the data is no longer required after the function is called. -
unbuffered functions, i.e.: those taking a ConstBuffers as a parameter.
These functions take astd::deque
ofasio::const_buffer
s that point to the data.
Therefore the data must NOT be temporary, it must exist until theMessage Sent
event, see Server Events.
An HTTP Server that uses the internal request router:
routing_http_server.cpp
An HTTP Server that incorporates the example code above:
example_http_server.cpp
An HTTPS Server that incorporates the example code above:
example_https_server.cpp
An HTTP Server that uses asio
strand wrapping and a thread pool: thread_pool_http_server.cpp