Search code examples
c++httpboost-asiopion-net

HTTP Client Request Response with pion-net c++


I want to create a simple HTTPClient with pion-net (Pion-net leverages boost::asio.).
Boot Version 1.49 Pion-net Version 4.11

My client should just be a able to:

  • send a HTTP Request (this is working)
  • receive the HTTP Response (not working)
  • asynchronous code is not a must, synchronous would be ok

This is what I got:

#include <iostream>
#include "boost/asio.hpp"
#include "boost/thread.hpp"
#include "pion/net/HTTPRequestWriter.hpp"
#include "pion/net/HTTPResponseReader.hpp"


void FinishedResponseReading(pion::net::HTTPResponsePtr httpResponsePtr,
                             pion::net::TCPConnectionPtr tcpConnectionPtr,
                             const boost::system::error_code& errorCode_ref)
{
    // ***************************
    // this code is never reached!
    // ***************************
    std::cout << errorCode_ref << std::endl;
    std::cout << httpResponsePtr->getContent() << std::endl;

    tcpConnectionPtr->finish();
    tcpConnectionPtr->close();
}


void FinishedRequestSending(const boost::system::error_code& error_code_ref,
                            pion::net::TCPConnectionPtr tcpConnectionPtr,
                            pion::net::HTTPRequest* httpRequest_ptr)
{
    // ***************************
    // this code is never reached!
    // ***************************
    pion::net::HTTPResponseReader::FinishedHandler fh =
        boost::bind(FinishedResponseReading, _1, _2, _3);
    pion::net::HTTPResponseReaderPtr httpResponseReaderPtr =
        pion::net::HTTPResponseReader::create(tcpConnectionPtr,
                                              *httpRequest_ptr,
                                              fh);
    httpResponseReaderPtr->receive();
}


int main()
{
    boost::asio::io_service io_service;

    // create and configure HTTPRequest
    pion::net::HTTPRequest* httpRequest_ptr = new pion::net::HTTPRequest();
    pion::net::HTTPRequestPtr httpRequestPtr(httpRequest_ptr);
    httpRequest_ptr->setResource("/someService");
    httpRequest_ptr->setMethod("PUT");

    // create TCPConnection
    pion::net::TCPConnection* tcpConnection_ptr =
        new pion::net::TCPConnection(io_service);
    pion::net::TCPConnectionPtr tcpConnectionPtr(tcpConnection_ptr);

    // create HTTPRequestWriter
    pion::net::HTTPRequestWriterPtr httpRequestWriterPtr(
        pion::net::HTTPRequestWriter::create(tcpConnectionPtr,
                                             httpRequestPtr,
                                             boost::bind(FinishedRequestSending, _1,
                                                         tcpConnectionPtr, 
                                                         httpRequest_ptr)));
    // needed?
    tcpConnection_ptr->setLifecycle(pion::net::TCPConnection::LIFECYCLE_KEEPALIVE);
    // connect to server
    tcpConnection_ptr->connect("192.168.1.14", 8080);
    // send payload
    httpRequestWriterPtr << "{\"someService\": \"something\"}";
    httpRequestWriterPtr->send();

    // ***********************************
    // working fine so far! server is getting payload and is sending a HTTP Response
    // but FinishedRequestSending is never reached
    // ***********************************

    // this is just to not exit immediately
    boost::this_thread::sleep(boost::posix_time::milliseconds(15000));

    // cleanup
    delete(httpRequest_ptr);
    delete(tcpConnection_ptr);

    return 0;
}

Solution

  • If you want to communicate synchronously you could do something like this:

    int main()
    {
        boost::asio::io_service io_service
        pion::net::TCPConnection tcpConnection(io_service);
    
        pion::net::HTTPRequest httpRequest;
        httpRequest.setResource("/server/resource");
        httpRequest.setMethod("PUT");
        httpRequest.setMinor(1);
        httpRequest.setMajor(1);
        httpRequest.setContent("test");
    
        boost::system::error_code ec;
        ec = tcpConnection.connect("192.168.1.1", 80); // blocks till connected or timed out
        if (!ec)
        {
            httpRequest.send(tcpConnection, ec); // never blocks
            if (!ec)
            {
                pion::net::HTTPResponse(httpRequest);
                httpResponse.receive(tcpConnection, ec); // this might block forever :-(
                // httpResponse.receive seems to be IO dependent, you can set your socket to timeout
                if (!ec)
                {
                    httpResponse.write(std::cout, ec);
                }
            }
        }
    }
    


    if however you need a little more sophisticated approach you could pion::net::HTTPResponseReader to wait asynch for a server response.
    header:

    class MyHTTPClient {
    public:
        void close();
        pion::net::HTTPResponsePtr blockingReceiveOrTimeout(pion::net::HTTPRequest httpRequest, boost::system::error_code& ec_ref);
        MyHTTPClient(boost::asio::ip::address aServerIP, unsigned int aServerPort);
        virtual ~MyHTTPClient();
    private:
        boost::asio::ip::address            mp_serverIP;
        unsigned int                    mp_serverPort;
        boost::asio::io_service             mp_ioService;
        pion::net::TCPConnectionPtr         mp_tcpConnectionPtr;
        pion::net::HTTPResponsePtr          mp_curr_httpResponsePtr;
        boost::system::error_code           mp_curr_errorCode;
    
        void finishedReceiveResponse(pion::net::HTTPResponsePtr httpResponsePtr, const boost::system::error_code& error_code_ref);
    
    };
    

    cpp:

    MyHTTPClient::MyHTTPClient(boost::asio::ip::address aServerIP, unsigned int aServerPort) : mp_serverIP(aServerIP), mp_serverPort(aServerPort)
    {
        mp_tcpConnectionPtr.reset(new pion::net::TCPConnection(mp_ioService));
        mp_tcpConnectionPtr->setLifecycle(pion::net::TCPConnection::LIFECYCLE_KEEPALIVE);
    }
    
    MyHTTPClient::~MyHTTPClient()
    {
         mp_tcpConnectionPtr->close();
    }
    
    void MyHTTPClient::close()
    {
        mp_tcpConnectionPtr->close();
    }
    
    pion::net::HTTPResponsePtr MyHTTPClient::blockingReceiveOrTimeout(pion::net::HTTPRequest httpRequest, boost::system::error_code& error_code_ref)
    {
        // reinit
        mp_curr_httpResponsePtr.reset();
        mp_ioService.reset();
        error_code_ref.clear();
    
        // connect to server if not already connectec
        if (!mp_tcpConnectionPtr->is_open())
        {
            error_code_ref = mp_tcpConnectionPtr->connect(mp_serverIP, mp_serverPort);
        }
        if (!error_code_ref)
        {
            // send Request
            httpRequest.send(*mp_tcpConnectionPtr.get(), error_code_ref, false);
    
            if (!error_code_ref)
            {
                // asynchronously wait for response (times out automatically)
                pion::net::HTTPResponseReader::FinishedHandler responseReaderFinishHandler = boost::bind(&MyHTTPClient::finishedReceiveResponse, this, _1, _3);
                const pion::net::HTTPRequest constHTTPRequest = httpRequest;
                pion::net::HTTPResponseReaderPtr httpResponseReaderPtr = pion::net::HTTPResponseReader::create(
                        mp_tcpConnectionPtr,
                        constHTTPRequest,
                        responseReaderFinishHandler);
                 httpResponseReaderPtr->receive();
                 mp_ioService.run();
            }
        }
        return mp_curr_httpResponsePtr;
    }
    
    void MyHTTPClient::finishedReceiveResponse(pion::net::HTTPResponsePtr httpResponsePtr, const boost::system::error_code& error_code_ref)
    {
        mp_curr_httpResponsePtr = httpResponsePtr;
    }