HTTP Server API =============== General ------- :mod:`cpp-netlib` includes and implements two distinct HTTP server implementations that you can use and embed in your own applications. Both HTTP Server implementations: * **Cannot be copied.** This means you may have to store instances of the HTTP Server in dynamic memory if you intend to use them as function parameters or pass them around in smart pointers of by reference. * **Assume that requests made are independent of each other.** None of the HTTP Server implementations support request pipelining (yet) so a single connection only deals with a single request. * **Are header-only and are compiled-into your application.** Future releases in case you want to upgrade the implementation you are using in your application will be distributed as header-only implementations, which means you have to re-compile your application to use a newer version of the implementations. The HTTP Servers have different semantics, and in some cases require different APIs from the supplied template parameters. Implementations --------------- There are two different user-facing template classes that differentiate the `Synchronous Servers`_ from the `Asynchronous Servers`_. Both templates take a single template parameter named ``Handler`` which describes the type of the Handler function object. There are two different Handler concepts, one concept for `Synchronous Servers`_ and another for `Asynchronous Servers`. The SynchronousHandler concept for `Synchronous Servers`_ is described by the following table: --------------- **Legend:** H The Handler type. h An instance of H. Req A type that models the Request Concept. Res A type that models the Response Concept. req An instance of Req. res An instance of Res. +----------------+-------------+----------------------------------------------+ | Construct | Return Type | Description | +================+=============+==============================================+ | ``h(req,res)`` | ``void`` | Handle the request; res is passed in as a | | | | non-const lvalue, which represents the | | | | response to be returned to the client | | | | performing the request. | +----------------+-------------+----------------------------------------------+ More information about the internals of the `Synchronous Servers`_ can be found in the following section. The AsynchronousHandler concept for `Asynchronous Servers`_ is described by the following table: --------------- **Legend:** H The Handler type. h An instance of H. Req A type that models the Request Concept. ConnectionPtr A type that models the Connection Pointer Concept. req An instance of Req. conn An instance of ConncetionPtr. +------------------+-------------+--------------------------------------------+ | Construct | Return Type | Description | +==================+=============+============================================+ | ``h(req, conn)`` | ``void`` | Handle the request; conn is a shared | | | | pointer which exposes functions for | | | | writing to and reading from the connection.| +------------------+-------------+--------------------------------------------+ More information about the internals of the `Asynchronous Servers`_ can be found in the following section. Synchronous Servers ------------------- The synchronous server implementation is represented by the template ``server`` in namespace ``boost::network::http``. The ``server`` template takes in a single template parameter named ``Handler`` which models the SynchronousHandler concept (described above). An instance of Handler is taken in by reference to the constructor of the HTTP server. This means the Handler is not copied around and only a single instance of the handler is used for all connections and requests performed against the HTTP server. .. warning:: It is important to note that the HTTP server does not implement any locking upon invoking the Handler. In case you have any state in the Handler that will be associated with the synchronous server, you would have to implement your own synchronization internal to the Handler implementation. This matters especially if you run the synchronous server in multiple threads. The general pattern of usage for the HTTP Server template is shown below: .. code-block:: c++ struct handler; typedef boost::network::http::server http_server; struct handler { void operator()( http_server::request const & req, http_server::response & res ) { // do something, and then edit the res object here. } }; More information about the actual HTTP Server API follows in the next section. It is important to understand that the HTTP Server is actually embedded in your application, which means you can expose almost all your application logic through the Handler type, which you can also initialize appropriately. API Documentation ~~~~~~~~~~~~~~~~~ The following sections assume that the following file has been included: .. code-block:: c++ #include And that the following typedef's have been put in place: .. code-block:: c++ struct handler_type; typedef boost::network::http::server http_server; struct handler_type { void operator()( http_server::request const & request, http_server::response & response ) { // do something here } }; Constructor ``````````` ``http_server(address, port, handler)`` Construct an HTTP Server instance, passing in the address and port as ``std::string const &`` and handler being of type ``handler_type`` but passed in as an lvalue reference. ``template client(ArgPack const & args)`` Pass in an argument pack. See supported parameters in the table below. +------------------------+------------------------------------------+--------------------------------------------------------------------------------------------------+ | Parameter Name | Type | Description | +========================+==========================================+==================================================================================================+ | _address | string_type | The hostname or IP address from which the server should be bound to. This parameter is required. | +------------------------+------------------------------------------+--------------------------------------------------------------------------------------------------+ | _port | string_type | The port to which the server should bind and listen to. This parameter is required. | +------------------------+------------------------------------------+--------------------------------------------------------------------------------------------------+ | _handler | ``Handler &`` | An lvalue reference to an instance of ``Handler``. This parameter is required. | +------------------------+------------------------------------------+--------------------------------------------------------------------------------------------------+ | _thread_pool | ``boost::network::utils::thread_pool &`` | An lvalue reference to an instance of ``boost::network::utils::thread_pool`` -- this is the | | | | thread pool from where the handler is invoked. This parameter is only applicable and required | | | | for ``async_server`` instances. | +------------------------+------------------------------------------+--------------------------------------------------------------------------------------------------+ | _io_service | ``boost::asio::io_service &`` | An optional lvalue to an instance of ``boost::asio::io_service`` which allows the server to use | | | | an already-constructed ``boost::asio::io_service`` instance instead of instantiating one that it | | | | manages. | +------------------------+------------------------------------------+--------------------------------------------------------------------------------------------------+ | _reuse_address | ``bool`` | A boolean that specifies whether to re-use the address and port on which the server will be | | | | bound to. This enables or disables the socket option for listener sockets. The default is | | | | ``false``. | +------------------------+------------------------------------------+--------------------------------------------------------------------------------------------------+ | _report_aborted | ``bool`` | A boolean that specifies whether the listening socket should report aborted connection attempts | | | | to the accept handler (an internal detail of cpp-netlib). This is put in place to allow for | | | | future-proofing the code in case an optional error handler function is supported in later | | | | releases of cpp-netlib. The default is ``false``. | +------------------------+------------------------------------------+--------------------------------------------------------------------------------------------------+ | _receive_buffer_size | ``int`` | The size of the socket's receive buffer. The default is defined by Boost.Asio and is | | | | platform-dependent. | +------------------------+------------------------------------------+--------------------------------------------------------------------------------------------------+ | _send_buffer_size | ``int`` | The size of the socket's send buffer. The default is defined by Boost.Asio and is | | | | platform-dependent. | +------------------------+------------------------------------------+--------------------------------------------------------------------------------------------------+ | _receive_low_watermark | ``int`` | The size of the socket's low watermark for its receive buffer. The default is defined by | | | | Boost.Asio and is platform-dependent. | +------------------------+------------------------------------------+--------------------------------------------------------------------------------------------------+ | _send_buffer_size | ``int`` | The size of the socket's send low watermark for its send buffer. The default is defined by | | | | Boost.Asio and is platform-dependent. | +------------------------+------------------------------------------+--------------------------------------------------------------------------------------------------+ | _non_blocking_io | ``bool`` | An optional bool to define whether the socket should use non-blocking I/O in case the platform | | | | supports it. The default is ``true``. | +------------------------+------------------------------------------+--------------------------------------------------------------------------------------------------+ | _linger | ``bool`` | An optional bool to determine whether the socket should linger in case there's still data to be | | | | sent out at the time of its closing. The default is ``true``. | +------------------------+------------------------------------------+--------------------------------------------------------------------------------------------------+ | _linger_timeout | ``int`` | An optional int to define the timeout to wait for socket closes before it is set to linger. | | | | The default is ``0``. | +------------------------+------------------------------------------+--------------------------------------------------------------------------------------------------+ To use the above supported named parameters, you'll have code that looks like the following: .. code-block:: c++ using namespace boost::network::http; // parameters are in this namespace boost::asio::io_service my_io_service; boost::network::utils::thread_pool pool(2); handler handler_instance; async_server instance(_address="0.0.0.0", _port="80", _handler=handler_instance, _io_service=my_io_service, _thread_pool=pool, _reuse_address=true); instance.run(); Public Members `````````````` The following definitions assume that a properly constructed ``http_server`` instance has been constructed in the following manner: .. code-block:: c++ handler_type handler; http_server server("127.0.0.1", "8000", handler); ``server.run()`` Run the HTTP Server event loop. This function can be run on multiple threads following the example: .. code-block:: c++ boost::thread t1(boost::bind(&http_server::run, &server)); boost::thread t2(boost::bind(&http_server::run, &server)); server.run(); t1.join(); t2.join(); ``server.stop()`` Stop the HTTP Server acceptor and wait for all pending requests to finish. Response Object ``````````````` The response object has its own public member functions which can be very helpful in certain simple situations. ``response = http_server::response::stock_reply(status, body)`` Code like the above should go inside the handler's ``operator()`` overload. The body parameter is an ``std::string``. The status parameter is any of the following values from the ``http_server::response`` enum ``status_type``: .. code-block:: c++ enum status_type { ok = 200, created = 201, accepted = 202, no_content = 204, multiple_choices = 300, moved_permanently = 301, moved_temporarily = 302, not_modified = 304, bad_request = 400, unauthorized = 401, forbidden = 403, not_found = 404, not_supported = 405, not_acceptable = 406, internal_server_error = 500, not_implemented = 501, bad_gateway = 502, service_unavailable = 503 }; The response object also has the following publicly accessible member values which can be directly manipulated by the handler. +------------------+----------------------+------------------------------------+ | Member Name | Type | Description | +==================+======================+====================================+ | status | ``status_type`` | The HTTP status of the response. | +------------------+----------------------+------------------------------------+ | headers | ``vector
`` | Vector of headers. [#]_ | +------------------+----------------------+------------------------------------+ | content | ``string_type`` [#]_ | The contents of the response. | +------------------+----------------------+------------------------------------+ .. [#] A header is a struct of type ``response_header``. An instance always has the members ``name`` and ``value`` both of which are of type ``string_type``. .. [#] ``string_type`` is ``boost::network::string::type``. Asynchronous Servers -------------------- The asynchronous server implementation is significantly different to the synchronous server implementation in three ways: #. **The Handler instance is invoked asynchronously**. This means the I/O thread used to handle network-related events are free to handle only the I/O related events. This enables the server to scale better as to the number of concurrent connections it can handle. #. **The Handler is able to schedule asynchronous actions on the thread pool associated with the server.** This allows handlers to perform multiple asynchronous computations that later on perform writes to the connection. #. **The Handler is able to control the (asynchronous) writes to and reads from the HTTP connection.** Because the connection is available to the Handler, that means it can write out chunks of data at a time or stream data through the connection continuously. The asynchronous server is meant to allow for better scalability in terms of the number of concurrent connections and for performing asynchronous actions within the handlers. If your applacation does not need to write out information asynchronously or perform potentially long computations, then the synchronous server gives a generally better performance profile than the asynchronous server. The asynchronous server implementation is available from a single user-facing template named ``async_server``. This template takes in a single template parameter which is the type of the Handler to be called once a request has been parsed from a connection. An instance of Handler is taken as a reference to the constructor similar to the synchronous server implementation. .. warning:: The asynchronous server implementation, like the synchronous server implementation, does not perform any synchronization on the calls to the Handler invocation. This means if your handler contains or maintains internal state, you are responsible for implementing your own synchronization on accesses to the internal state of the Handler. The general pattern for using the ``async_server`` template is shown below: .. code-block:: c++ struct handler; typedef boost::network::http::async_server http_server; struct handler { void operator()( http_server::request const & req, http_server::connection_ptr connection ) { // handle the request here, and use the connection to // either read more data or write data out to the client } }; API Documentation ~~~~~~~~~~~~~~~~~ The following sections assume that the following file has been included: .. code-block:: c++ #include #include And that the following typedef's have been put in place: .. code-block:: c++ struct handler_type; typedef boost::network::http::server http_server; struct handler_type { void operator()( http_server::request const & request, http_server::connection_ptr connection ) { // do something here } }; Constructor ``````````` ``http_server(address, port, handler, thread_pool)`` Construct an HTTP Server instance, passing in the address and port as ``std::string const &`` and handler being of type ``handler_type`` but passed in as an lvalue reference. The ``thread_pool`` parameter is an instance of ``boost::network::utils::thread_pool`` that has been previously instantiated. .. note:: The ``boost::network::utils::thread_pool`` has a single constructor parameter which is the number of threads to run the thread pool. Public Members `````````````` The following definitions assume that a properly constructed ``http_server`` instance has been constructed in the following manner: .. code-block:: c++ handler_type handler; boost::network::utils::thread_pool thread_pool(2); http_server server("127.0.0.1", "8000", handler, thread_pool); ``server.run()`` Run the HTTP Server event loop. This function can be run on multiple threads following the example: .. code-block:: c++ boost::thread t1(boost::bind(&http_server::run, &server)); boost::thread t2(boost::bind(&http_server::run, &server)); server.run(); t1.join(); t2.join(); ``server.stop()`` Stop the HTTP Server acceptor and wait for all pending requests to finish. Connection Object ````````````````` The connection object has its own public member functions which will be the primary means for reading from and writing to the connection. ``template write(Range range)`` The connection object exposes a function ``write`` that can be given a parameter that adheres to the Boost.Range_ ``Single Pass Range`` Concept. The write function, although it looks synchronous, starts of a series of asynchronous writes to the connection as soon as the range is serialized to appropriately sized buffers. To use this in your handler, it would look something like this: .. code-block:: c++ connection->write("Hello, world!"); std::string sample = "I have a string!"; connection->write(sample); ``template void write(Range range, Callback callback)`` The connection object also exposes a function ``write`` that can be given a parameter that adheres to the Boost.Range_ ``Single Pass Range`` Concept, as well as a Callback function that returns ``void`` and takes a ``boost::system::error_code`` as a parameter. This overload of ``write`` is useful for writing streaming applications that send out chunks of data at a time, or for writing data that may not all fit in memory right away. ``template void read(ReadCallback callback)`` The connection object has a function ``read`` which can be used to read more information from the connection. This ``read`` function takes in a callback that can be assigned to a Boost.Function_ with the signature ``void(input_range,error_code,size_t,connection_ptr)``. The following list shows what the types actually mean: * **input_range** -- ``boost::iterator_range`` : The range that denotes the data read from the connection. * **error_code** -- ``boost::system::error_code`` : The error code if there were any errors encountered from the read. * **size_t** -- ``std::size_t`` : The number of bytes transferred. * **connection_ptr** -- ``http_server::connection_ptr`` : A handle to the current connection, so that it is kept alive at the time of the read callback invocation. This interface is useful when doing reads of uploaded data that can be potentially large and may not fit in memory. The read handler is then responsible for dealing with the chunks of data available from the connection. ``void set_status(status_t new_status)`` The ``set_status`` function takes a parameter of type ``status_t`` which is an enum type nested in ``http_status::connection`` which is given in the following code listing. .. code-block:: c++ enum status_t { ok = 200 , created = 201 , accepted = 202 , no_content = 204 , multiple_choices = 300 , moved_permanently = 301 , moved_temporarily = 302 , not_modified = 304 , bad_request = 400 , unauthorized = 401 , forbidden = 403 , not_found = 404 , not_supported = 405 , not_acceptable = 406 , internal_server_error = 500 , not_implemented = 501 , bad_gateway = 502 , service_unavailable = 503 }; .. note:: You may set and re-set the status several times as long as you have not set the headers or sent data through the connection. If you do this after data has already been set, the function will throw an instance of ``std::logic_error``. ``template void set_headers(Range range)`` The ``set_headers`` function takes a Single Pass Range of ``boost::network::http::response_header`` instances and linearizes them to a buffer with at most ``BOOST_NETWORK_HTTP_SERVER_CONNECTION_HEADER_BUFFER_MAX_SIZE`` and immediately schedules an asynchronous write once that is done. The function throws an instance of ``std::logic_error`` if you try to set the headers for a connection more than once. .. _Boost.Range: http://www.boost.org/libs/range .. _Boost.Function: http://www.boost.org/libs/function