Problems making a sync http request through proxy using Boost Beast
Asked Answered
W

1

6

I've modified the http_sync example to connect through a proxy, I've tested with wireshark and the problem is that after I send the http connect request, the proxy returns with the code 200 OK, and my program cant read that, it gets stuck waiting on the http::read for about 1 minute and then the server disconnects me. I've tried going around that and just not reading the response, but then when I try to do the ssl handshake I get an error:unknown protocol(as far as wireshark shows, my ssl handshake sends a client hello, and then I receive the full server hello but my program sends a FIN in the meantime and gives me the error, I think its because he read the 200 OK instead of the server's answer and got confused). so I guess my question is, is there a way to get around this problem? I cant figure out why http::read gets stuck.It works normally without the proxy in the middle. Here is my code:

using tcp = boost::asio::ip::tcp;       // from <boost/asio/ip/tcp.hpp>
namespace ssl = boost::asio::ssl;       // from <boost/asio/ssl.hpp>
namespace http = boost::beast::http;    // from <boost/beast/http.hpp>


int main(int argc, char** argv)
{
try
{
    // Check command line arguments.
    auto const target = "www.discogs.com:443";
    auto const host = "192.116.142.153";
    auto const port = "443";
    int version = 11;

    // The io_context is required for all I/O
    boost::asio::io_context ioc;

    // These objects perform our I/O

    ssl::context ctx{ ssl::context::sslv23_client };

    load_root_certificates(ctx);

    tcp::resolver resolver{ ioc };
    ssl::stream<tcp::socket> stream{ ioc, ctx };


    tcp::resolver::iterator sock = resolver.resolve("192.116.142.153", "8080");


    boost::asio::connect(stream.next_layer(), sock);

    http::request<http::string_body> req1{ http::verb::connect, target, version };
    req1.set(http::field::host, target);

    http::write(stream.next_layer(), req1);
    boost::beast::flat_buffer buffer;
    http::response<http::dynamic_body> res;
    http::read(stream.next_layer(), buffer, res);

    // Write the message to standard out
    std::cout << res << std::endl;

    // Perform the SSL handshake
    stream.handshake(ssl::stream_base::client);

    // Set up an HTTP GET request message
    http::request<http::string_body> req{ http::verb::get, target, version };
    req.set(http::field::host, host);
    req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);

    // Send the HTTP request to the remote host
    http::write(stream, req);

    // This buffer is used for reading and must be persisted
    //boost::beast::flat_buffer buffer;

    // Declare a container to hold the response
    //http::response<http::dynamic_body> res;

    // Receive the HTTP response
    http::read(stream, buffer, res);

    // Write the message to standard out
    std::cout << res << std::endl;
    getchar();
    // Gracefully close the stream
    boost::system::error_code ec;
    stream.next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_send);
    if (ec == boost::asio::error::eof)
    {
        // Rationale:
        // https://mcmap.net/q/419657/-boost-asio-ssl-async_shutdown-always-finishes-with-an-error
        ec.assign(0, ec.category());
    }
    if (ec)
        throw boost::system::system_error{ ec };

    // If we get here then the connection is closed gracefully
}
catch (std::exception const& e)
{
    std::cerr << "Error: " << e.what() << std::endl;

    return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Watterson answered 14/4, 2018 at 23:14 Comment(0)
B
4

Searching the docs for proxy put me on the trail of skip(). This lead me to use my own response parser instance:

http::response<http::empty_body> res;
http::parser<false, http::empty_body> p(res);
p.skip(true);
http::read(stream.next_layer(), buffer, p);

That works. Well, I changed the request target to something that works. In this case, I retrieved https://www.boost.org/users/license.html via your Fortinet proxy:

#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>

using tcp = boost::asio::ip::tcp;    // from <boost/asio/ip/tcp.hpp>
namespace ssl = boost::asio::ssl;    // from <boost/asio/ssl.hpp>
namespace http = boost::beast::http; // from <boost/beast/http.hpp>

static void load_root_certificates(ssl::context &ctx);

int main() try {
    // Check command line arguments.
    auto const target = "www.boost.org:443";
    auto const host = "www.boost.org";
    int version = 11;

    // The io_context is required for all I/O
    boost::asio::io_context ioc;

    // These objects perform our I/O

    ssl::context ctx{ ssl::context::sslv23_client };

    load_root_certificates(ctx);

    tcp::resolver resolver{ ioc };
    ssl::stream<tcp::socket> stream{ ioc, ctx };

    stream.next_layer().connect({boost::asio::ip::address_v4::from_string("192.116.142.153"), 8080});

    {
        http::request<http::string_body> req1{ http::verb::connect, target, version };
        req1.set(http::field::host, target);

        http::write(stream.next_layer(), req1);

        boost::beast::flat_buffer buffer;

        http::response<http::empty_body> res;
        http::parser<false, http::empty_body> p(res);
        p.skip(true);
        http::read(stream.next_layer(), buffer, p);

        // Write the message to standard out
        std::cout << res << std::endl;
    }

    // Perform the SSL handshake
    stream.handshake(ssl::stream_base::client);

    // Set up an HTTP GET request message
    {
        http::request<http::string_body> req{ http::verb::get, "/users/license.html", version };
        req.set(http::field::host, host);
        req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);

        // Send the HTTP request to the remote host
        http::write(stream, req);

        boost::beast::flat_buffer buffer;
        http::response<http::dynamic_body> res;
        http::read(stream, buffer, res);
        // Write the message to standard out
        std::cout << res << std::endl;
    }

    getchar();

    // Gracefully close the stream
    boost::system::error_code ec;
    stream.next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_send);
    if (ec == boost::asio::error::eof) {
        // Rationale:
        // https://mcmap.net/q/419657/-boost-asio-ssl-async_shutdown-always-finishes-with-an-error
        ec.assign(0, ec.category());
    }
    if (ec)
        throw boost::system::system_error{ ec };
    // If we get here then the connection is closed gracefully
} catch (std::exception const &e) {
    std::cerr << "Error: " << e.what() << std::endl;
    return EXIT_FAILURE;
}


namespace detail {

static void load_root_certificates(ssl::context &ctx, boost::system::error_code &ec) {
    std::string const cert =
        /*  This is the DigiCert root certificate.

            CN = DigiCert High Assurance EV Root CA
            OU = www.digicert.com
            O = DigiCert Inc
            C = US

            Valid to: Sunday, ?November ?9, ?2031 5:00:00 PM

            Thumbprint(sha1):
            5f b7 ee 06 33 e2 59 db ad 0c 4c 9a e6 d3 8f 1a 61 c7 dc 25
        */
        "-----BEGIN CERTIFICATE-----\n"
        "MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs\n"
        "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"
        "d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j\n"
        "ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL\n"
        "MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3\n"
        "LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug\n"
        "RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm\n"
        "+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW\n"
        "PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM\n"
        "xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB\n"
        "Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3\n"
        "hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg\n"
        "EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF\n"
        "MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA\n"
        "FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec\n"
        "nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z\n"
        "eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF\n"
        "hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2\n"
        "Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe\n"
        "vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep\n"
        "+OkuE6N36B9K\n"
        "-----END CERTIFICATE-----\n"
        /*  This is the GeoTrust root certificate.

            CN = GeoTrust Global CA
            O = GeoTrust Inc.
            C = US
            Valid to: Friday, ‎May ‎20, ‎2022 9:00:00 PM

            Thumbprint(sha1):
            ‎de 28 f4 a4 ff e5 b9 2f a3 c5 03 d1 a3 49 a7 f9 96 2a 82 12
        */
        "-----BEGIN CERTIFICATE-----\n"
        "MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs\n"
        "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"
        "d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j\n"
        "ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL\n"
        "MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3\n"
        "LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug\n"
        "RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm\n"
        "+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW\n"
        "PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM\n"
        "xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB\n"
        "Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3\n"
        "hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg\n"
        "EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF\n"
        "MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA\n"
        "FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec\n"
        "nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z\n"
        "eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF\n"
        "hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2\n"
        "Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe\n"
        "vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep\n"
        "+OkuE6N36B9K\n"
        "-----END CERTIFICATE-----\n";

    ctx.add_certificate_authority(boost::asio::buffer(cert.data(), cert.size()), ec);
    if (ec)
        return;
}

} // namespace detail

static void load_root_certificates(ssl::context &ctx) {
    boost::system::error_code ec;
    detail::load_root_certificates(ctx, ec);
    if (ec)
        throw boost::system::system_error{ ec };
}

Prints

HTTP/1.1 200 OK


HTTP/1.1 200 OK
Date: Sun, 15 Apr 2018 00:32:52 GMT
Server: Apache/2.2.15 (CentOS)
Accept-Ranges: bytes
Connection: close
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8

8ee3
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
  <title>Boost Software License</title>
  <meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
  <link rel="icon" href="/favicon.ico" type="image/ico" />
  <link rel="stylesheet" type="text/css" href="../style-v2/section-boost.css" />
  <!--[if IE 7]> <style type="text/css"> body { behavior: url(/style-v2/csshover3.htc); } </style> <![endif]-->
</head><!--
Note: Editing website content is documented at:
https://www.boost.org/development/website_updating.html
-->

<body>
  <div id="heading">
    <div class="heading-inner">
  <div class="heading-placard"></div>

  <h1 class="heading-title">
  <a href="/">
  <img src="/gfx/space.png" alt= "Boost C++ Libraries" class="heading-logo" />
  <span class="heading-boost">Boost</span>
  <span class="heading-cpplibraries">C++ Libraries</span>
  </a></h1>

  <p class="heading-quote">
  <q>...one of the most highly
  regarded and expertly designed C++ library projects in the
  world.</q> <span class="heading-attribution">&mdash; <a href=
  "http://www.gotw.ca/" class="external">Herb Sutter</a> and <a href=
  "http://en.wikipedia.org/wiki/Andrei_Alexandrescu" class="external">Andrei
  Alexandrescu</a>, <a href=
  "http://safari.awprofessional.com/?XmlId=0321113586" class="external">C++
  Coding Standards</a></span></p>
</div>

  </div>

  <div id="body">
    <div id="body-inner">
      <div id="content">
        <div class="section" id="intro">
          <div class="section-0">
            <div class="section-title">
              <h1>Boost Software License</h1>
            </div>

            <div class="section-body">
              <ul class="toc">
                <li><a href="../LICENSE_1_0.txt">License text</a></li>

(remainder snipped)

Bookcase answered 15/4, 2018 at 0:35 Comment(5)
wow thanks, that works perfectly, btw what does that skip actually do? im trying to realize what really happends with the packages and why regular read gets blocked there.Watterson
It's still regular read. I linked to the docs of skip and even made sure I explained how I discovered it. (I'm not sure what "packages" are)Bookcase
by packages I refer to the messages being passed over the internet, but nvm that, I understand how you find it, but I cant realize why it is necesairy, the sync read that I've used without the parser and skip should see the response from proxy, im still confused as to why it didn't. I like to understand how things work not only to get them to work, I see your way works and il just use the parser and skip in the future when dealing with proxy's, I am just asking why is it necesairyWatterson
ok im an idiot, didn't see you linked the skip function, I had a feeling the read I used failed because the message contained no body, thanks and sorry for the stupid questions :)Watterson
Ah :) I was just going to quote the documentation. +100 for wanting to understand. ("Packages" are "packets" in that context). Cheers!Bookcase

© 2022 - 2024 — McMap. All rights reserved.