Question

I have a http client that I wrote in python in tornado framework:

http_client = httpclient.HTTPClient()
request = httpclient.HTTPRequest("http://127.0.0.1:8000", method='PUT', body=str("data"))
response = http_client.fetch(request)

on the other side I have an asynchronous server that I wrote in c++ using cpp-netlib. It basically has to read the request and print its body

class Server;
typedef http::async_server<Server> server;

class Server {
public:
void operator()(server::request const & request, server::connection_ptr connection) 
{
    boost::shared_ptr<connection_handler> h(new connection_handler());
    (*h)(request, connection);

    server::response_header headers[] = { {"Connection","close"} ,{"Content-Type", "text/plain"} };
    connection->set_headers(boost::make_iterator_range(headers, headers+2));
    connection->set_status(server::connection::accepted);
    connection->write("helloworld");        
}

int main() 
{   
    Server handler;
    server::options options(handler);
    server instance(
    options.thread_pool(boost::make_shared<utils::thread_pool>(5))
           .address("0.0.0.0")
           .port("8000"));        

    instance.run();
    return 0;
}

and the connection handler looks like this:

struct connection_handler : boost::enable_shared_from_this<connection_handler> 
{

    struct read_post_callback 
    {
        typedef boost::shared_ptr<connection_handler> handler_ptr_t;
        read_post_callback(handler_ptr_t  handler_ptr) : handler(handler_ptr) {}

        void operator()(server::connection::input_range range, boost::system::error_code error, size_t size, server::connection_ptr conn)
        {
            handler->read_sofar += size;
            handler->cond.notify_one();
        }

        handler_ptr_t handler;
    };

    void operator()(server::request const &req, server::connection_ptr conn) 
    {
        int cl;
        server::request::headers_container_type const &hs = req.headers;
        for(server::request::headers_container_type::const_iterator it = hs.begin(); it!=hs.end(); ++it) 
        {
            if(boost::to_lower_copy(it->name)=="content-length") 
            {
                cl = boost::lexical_cast<int>(it->value);
                break;
            }
        }
        cout<< req.body << endl;
        read_sofar = 0;
        while(read_sofar<cl) 
        {
            boost::unique_lock<boost::mutex> lock(mtx);
            server::connection::read_callback_function cc = read_post_callback(shared_from_this());
            conn->read(cc);

            cond.wait(lock);
        }
    }

    int read_sofar;
    boost::condition_variable cond;
    boost::mutex mtx;
};

But request body is always empty and instead of the response I send back ("hello world") most of the time I get something like this (the number could be different)

Error: HTTP 0: Unknown

Can you tell me why the body is empty or why the response doesn't reach?

EDIT:

Why the body was empty is in the answer. What I was doing wrong that I wouldn't receive the response was this: I was setting the connection status after setting connection headers in my server. Just swapped their order and it worked like a charm.

server::response_header headers[] = { {"Connection","close"} ,{"Content-Type", "text/plain"} };

connection->set_status(server::connection::accepted);
connection->set_headers(boost::make_iterator_range(headers, headers+2));
connection->write("helloworld");
Était-ce utile?

La solution

With cpp-netlib, asynchronous reads have to be done as follows. req.body doesn't get loaded with the data read from the asynchronous. So have to have your own holder for data.

struct connection_handler : boost::enable_shared_from_this<connection_handler>
{
        std::string                 body;
        boost::condition_variable   cond;
        boost::mutex                mtx;
        size_t                      read_sofar;

        connection_handler(const server::request& req, const server::connection_ptr& conn) :
                conn(conn),
                body("")
        {
        }

        ~connection_handler()
        {
        }

        void operator()(const server::request& req, const server::connection_ptr& conn)
        {
                size_t cl = 0;
                auto const& hs = req.headers;
                for (auto it = hs.begin(); it != hs.end(); ++it)
                {
                        if (boost::to_lower_copy(it->name)=="content-length")
                        {
                                cl = boost::lexical_cast<size_t>(it->value);
                                break;
                        }
                }

                read_sofar = 0;
                while (read_sofar < cl)
                {
                        boost::unique_lock<boost::mutex> lock(mtx);
                        read_chunk(cl - read_sofar, conn);

                        LogTrace(("gonna wait"));
                        cond.wait(lock);
                        LogTrace(("wokeup"));
                }
        }

        void read_chunk(size_t left2read, server::connection_ptr conn)
        {
                LogTrace(("left2read: %ld", left2read));
                conn->read(
                           boost::bind(
                                       &connection_handler::handle_post_read,
                                       connection_handler::shared_from_this(),
                                       _1, _2, _3, conn, left2read
                                      )
                          );
        }

        void handle_post_read(server::connection::input_range range,
                              boost::system::error_code error,
                              size_t size,
                              server::connection_ptr conn,
                              size_t left2read)
        {
                if (!error)
                {
                        LogTrace(("readSize: %ld", size));
                        body.append(boost::begin(range), size);
                        size_t left = left2read - size;
                        read_sofar += size;
                        if (left > 0)
                        {
                                read_chunk(left, conn);
                        }
                        else
                        {
                                cond.notify_one();
                        }
                }
                LogTrace((error.message()));
        }
};

Also you may want to change BOOST_NETWORK_HTTP_SERVER_CONNECTION_BUFFER_SIZE defined in boost/network/protocol/http/serversync_connection.hpp to a higher value so that you can avoid multiple context switches. It is not configurable at present.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top