HTTP Listener

An HTTP listener is a C++ object that accepts messages directed at a particular URI, which we will refer to as the “listener URI.” A service on a given port may have many HTTP listeners installed, or just one: the determining factor is mainly whether the service wants to do its own message dispatch or rely on the C++ REST SDK to perform it.

HTTP listeners APIs can be found in the class web::http::experimental::listener.

Creating a Listener

To create a http_listener instance, you need to provide the URI where the listener will be accepting requests. The constructor returns an instance of http_listener, which is not actively listening for messages yet.

http_listener listener(L"http://localhost/path_1");

To start listening, your code has to call open() on the listener. Here’s an example:

    listener.open().then([&listener]()
    {
        ...
    });

When your service is ready to shut down, call listener.close();

Dispatch

The C++ REST SDK does two kinds of dispatch: path-based and method-based. What the former means is that the library considers the incoming resource path of a message and matches it against the active listeners. The listener with the longest matching path wins and gets to handle the message.

The basic HTTP listener implementation will then perform method-based dispatch, which is nothing fancier than sending GET messages to handle_get(), PUT messages to handle_put(), etc. These functions are not names of virtual functions, they are simply a convention that we have been using in our code and documentation. In reality, they are established by a call to the support() function on an http_listener instance:

listener.support(methods::GET, [] (http_request request) 
    {
        request.reply(status_codes::OK, U("Hello, World!"));

    });

Simple listeners may be defined entirely from lambdas, but it may be necessary to build more complex services that contain more data from a class. This is straight-forward using the STL bind() functionality:

#include <cpprest\http_listener.h>

using namespace web::http::experimental::listener;
using namespace web::http;
using namespace web;

class MyListener 
{
public:
    MyListener(const http::uri& url);

private:
    void handle_get(http_request request);
    void handle_put(http_request request);
    void handle_post(http_request request);
    void handle_delete(http_request request);
 
    http_listener m_listener;        
};

MyListener::MyListener(const http::uri& url) : m_listener(http_listener(url)) 
   
{
    m_listener.support(methods::GET,
                       std::tr1::bind(&MyListener::handle_get,
                                      this,
                                      std::tr1::placeholders::_1));
    m_listener.support(methods::PUT,
                       std::tr1::bind(&MyListener::handle_put,
                                      this,
                                      std::tr1::placeholders::_1));
    m_listener.support(methods::POST,
                       std::tr1::bind(&MyListener::handle_post,
                                      this,
                                      std::tr1::placeholders::_1));
    m_listener.support(methods::DEL,
                       std::tr1::bind(&MyListener::handle_delete,
                                      this,
                                      std::tr1::placeholders::_1));
}

Processing Requests

The bulk of service work goes on within the handle_xxx methods. Typically, a request is serviced by accepting the message as an argument to a handler function, examining the resource, any headers, and the message body, if available. Then, actions are taken and a response is formulated. Each handler gets a single argument: an instance of ‘http_request,’ found in the web::http namespace. If you are familiar with our Http Client, the request and response classes are the same.

The handlers return nothing and the return does not signal completion of servicing the request. Instead, the completion of the request is confirmed by calling the reply() function on the message that was passed in. This does not have to happen before the handler function returns – request handling is asynchronous and the purpose of the handlers is only to dispatch requests to the right logic, not to manage the lifetime of the request.

A trivial handler that denies access to all resources may look like this:

void MyListener::handle_get(http_request message)
{
    message.reply(status_codes::NotFound);
};

This is another version of “Hello World:”

void MyListener::handle_get(http_request message)
{
    message.reply(status_codes::OK, U("Hello, World!"));
};

The proper use of various HTTP status codes is not a topic of this document – such conventions are for you to decide; there are general guidelines in the HTTP 1.1 RFC. When possible and relevant, it is always a good idea to stay as close to those as possible rather than coming up with your own ad hoc conventions.

To learn more about the HTTP Listener browse our source code (http_listener.cpp) and tests.

Last edited Mar 15, 2014 at 12:54 AM by stevetgates, version 9