chriskohlhoff / asio

Asio C++ Library
http://think-async.com/Asio
4.96k stars 1.22k forks source link

How to read till end of data on tcp socket with boost asio #1431

Closed MortezaBashsiz closed 8 months ago

MortezaBashsiz commented 8 months ago

Hi I am writing a program that acts as a https_proxy and bypass the filtering by hiding original requests inside a fake HTTP request. My problem is that I am not able to detect end of data during the SSL connection. Imagine I don't know what is the protocol (http/https/ftp or anything else), I want to make sure that I just read all data from socket. I do not have any point to detect end of the data like "\r\n\r\n" which you can use in HTTP or FTP protocols to detect end of data. I prepared a code to simulate what I am testing. It will listen on TCP/127.0.0.10:8080

#include <iostream>

#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/bind/bind.hpp>

class TCPConnection
    : public boost::enable_shared_from_this<TCPConnection>
{
public:
    using pointer =  boost::shared_ptr<TCPConnection>;

    static pointer create(boost::asio::io_context& io_context)
    {
        return pointer(new TCPConnection(io_context));
    }

    boost::asio::ip::tcp::socket& socket()
    {
        return socket_;
    }

    void start()
    {
        std::cout << "start" << std::endl;
        doRead();
    }

    void doRead()
    {
        std::cout << "doRead" << std::endl;
        while(true){
            boost::system::error_code error;
            auto size = boost::asio::read(
                socket_,
                readBuffer_,
                boost::asio::transfer_exactly(1),
                error
            );
            std::cout << size << std::endl;
            if (error == boost::asio::error::eof || size == 0)
                break;
            else if (error)
            {
                std::cout << "ERROR" << error.what() << std::endl;
            }
        }
      std::cout << "END" << std::endl;
    }

private:
    explicit TCPConnection(boost::asio::io_context& io_context)
        : socket_(io_context)
    { }

    boost::asio::ip::tcp::socket socket_;
    boost::asio::streambuf readBuffer_, writeBuffer_;
};

class TCPServer 
{
public:
    using pointer =  boost::shared_ptr<TCPServer>;

    static pointer create(boost::asio::io_context& io_context)
    {
        return pointer(new TCPServer(io_context));
    }

private:
    explicit TCPServer(boost::asio::io_context& io_context)
        : io_context_(io_context),
            acceptor_(
                io_context,
                boost::asio::ip::tcp::endpoint(
                    boost::asio::ip::address::from_string("127.0.0.10"),
                    8080
                )
            )
    {
        startAccept();
    }

    void startAccept()
    {
        std::cout << "startAccept" << std::endl;

        TCPConnection::pointer newConnection =
            TCPConnection::create(io_context_);

        acceptor_.async_accept(newConnection->socket(),
            boost::bind(&TCPServer::handleAccept, 
                this, 
                newConnection,
                boost::asio::placeholders::error)
            );
    }

    void handleAccept(TCPConnection::pointer newConnection,
        const boost::system::error_code& error)
    {
        if (!error)
        {
            boost::asio::socket_base::keep_alive option(true);
            newConnection->socket().set_option(option);
            newConnection->start();
        }
        startAccept();
    }

        boost::asio::io_context& io_context_;
        boost::asio::ip::tcp::acceptor acceptor_;
};

int main(int argc, char const *argv[])
{
    boost::asio::io_context io_context_;
    TCPServer::pointer tcpServer_ = TCPServer::create(io_context_);
    io_context_.run();
}

Then simply set the http_proxy

export https_proxy="http://127.0.0.10:8080"

and try to curl somewhere

curl https://www.google.com

My expectation is to see END in output of c++ code immediately, but you will never see it till you kill your curl command. I already tried to read byte by byte and parse it.

Is there something that I am missing here? Is my expectation correct from behavior of read()?

MortezaBashsiz commented 8 months ago

Read operation from socket will return zero only at end of stream and not before it. Here it was misunderstanding from my side, I expect to get zero immediately at end of message which is not possible.