C++ library to populate SSL stream with valid HTTP requests - c++

I am using Boost.Asio ssl streams, and got a working encrypted socket from which I can send and receive bytes.
I successfully did a GET request with the following code :
// Construct HTTP request (using vanilla std::ostream)
std::ostream request_stream(&request);
request_stream << "GET / HTTP/1.0\r\n";
request_stream << "Host: " << argv[1] << "\r\n";
...
// Send request
ssl::stream<tcp::socket> socket
boost::asio::write(socket, request);
And I would now love to find a small C++ library that would provide an easy way to get the ostream loaded with a valid HTTP request !

Since you're already using Boost.Asio, consider using Boost.Beast which is a low-level HTTP library. Example of sending a GET request using Boost.Beast:
using namespace boost::beast;
// Set up an HTTP GET request message
http::request<http::empty_body> req{http::verb::get, "/", 11};
req.set(http::field::host, "www.example.com");
req.set(http::field::user_agent, "Beast/1.0);
// Send the HTTP request to the remote host
http::write(socket, req);
The full example is here:
https://github.com/boostorg/beast/blob/master/example/http/client/sync/http_client_sync.cpp
Beast is available in Boost versions 1.66 and later. Here is the documentation page, which includes many examples:
http://www.boost.org/doc/libs/1_66_0/libs/beast/doc/html/index.html
If you really want to write the HTTP request to a std::ostream, Beast supports operator<< for HTTP messages (it is mainly there for debugging), but I think you are better off just writing it directly to the ip::tcp::socket or ssl::stream using Beast.

Related

Using boost::beast::websocket to send http post request

I've got the following class member
boost::beast::websocket::stream<boost::beast::tcp_stream> ws_;
which gets instantiated on class c'tor using boost::asio::io_service as parameter.
I assume that all the websocket protocol is implemented on top of http so i was wondering if it's possible to extract the socket out of ws_ and use it to send regular http POST request ?
I've found an example that actually create the websocket out of simple tcp::socket here, so i guess it's doable, but how exactly ?
One good use-case for this for this approach is to fallback to simple HTTP requests (GET/POST) in case the websocket handshake failed (assuming it's supported by the server)
thanks

Get protocol of request in boost/beast

I'm having trouble to get the protocol (HTTP or HTTPS) of a request in boost beast webserver.
I can get the host using:
req[http::field::host]
but no such luck with the protocol.
req[http::field::protocol]
is empty.
There is no http::field for HTTPS.
For using HTTPS basically you are doing following (example):
1.Create io_context and ssl_context
boost::asio::io_context ioc;
boost::asio::ssl::context ctx(boost::asio::ssl::context::tlsv12_client)
2.create ssl stream from io_context and ssl::context
boost::beast::ssl_stream<boost::beast::tcp_stream> stream(ioc, ctx)
3.create tcp connection using lowest_layer
beast::get_lowest_layer(stream).connect(...endpoints iterator from query resolver..)
4.do the ssl handshake
stream.handshake(ssl::stream_base::client);
5.now you can make requests writing to stream as usual:
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);
Check the official boost beast examples.
Considering the fact that you are behind a nginx server, Beast cannot solve your problem. But the additional headers that nginx will send to your server will.
You may rely on the HTTP_X_FORWARDED_PROTO or HTTP_X_FORWARDED_SSL headers. Here's what I came up with by following an answer to a similar question:
using namespace boost::beast;
using namespace std;
string find_scheme(const http::request<http::string_body>& request)
{
http::request<http::string_body>::const_iterator header;
header = request.find("HTTP_X_FORWARDED_PROTO");
if (header != request.end())
return header->value().data();
else
{
header = request.find("HTTP_X_FORWARDED_SSL");
if (header != request.end())
return header->value() == "on" ? "https" : "http";
}
return "http";
}

SSL tunnel with Boost::Beast

I want to connect to a proxy server that only allows HTTP connections, to speak with the target server by HTTPS.
The proxy server documentation states that the only way to do that is by means of the HTTP Connect verb (they are planning to add direct HTTPS connections to the proxy server itself, but for the moment only HTTP connections are allowed).
In my C++ program, I successfully connected and worked with the target server using ssl_stream's during a couple of months, using boost::asio without boost::beast, but I want now to use a proxy using boost::beast to make things easier; so, I now how to work with boost::asio but I'm a boost::beast newbie (and I don't fully understand how SSL works either).
The think is that, in my understanding, when you use a ssl_stream, you encript the whole communication, however, what I need now is to insert the encrypted message within the CONNECT HTTP body, and I don't know how to do that.
I've readed that this has something to do with the lowest_layer/next_layer thing but I'm not sure.
Could anybody provide an example of a full read/write connection with a proxy-server? or at least further clarifications?
Declare a variable for the connection (ioc is the io_context)
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> stream{ioc};
Build a CONNECT HTTP request message (req) using Beast
Send the request to the proxy in plain-text (note next_layer())
boost::beast::http::write(stream.next_layer(), req);
Read the HTTP response from the proxy
If the response has OK status, the tunnel is established
Now perform the SSL handshake:
stream.handshake(boost::asio::ssl::stream_base::client);
At this point you can write HTTP requests to stream and read HTTP responses from stream using Beast as normal (do not use next_layer() again).

C++ - Sending HTTP POST/GET after connection has closed

I've been searching for a solution on StackOverFlow and couldn't seem to find an answer, I am using WinSock2 to log into a website and the server responds with "Connection: close" (Even if I send Connection: keep-alive) in the header. Any messages attempting to recv after returns 0. (0 = Graceful close)
Questions:
Is the connection suppose to drop after a POST request?
How do you send subsequent GET/POST requests after it has dropped?
So do you have to recycle the socket and re-establish everything again like my example below?
Example of list of events (that i'd imagine i'd need to do):
WSAStartup
Find Address
Create socket
Connect Socket with address
Send Post Request
---Connection Closes after Post---
Destroy Socket
Create socket
Connect Socket with address
Send Get Request with Authentication Cookie
This is what I picture the event chain would look like, but I am not 100% sure how browsers handle all this. But I learn from other users experience and input so if anyone knows exactly what happens let me know. Thank you for your time,
Is the connection suppose to drop after a POST request?
It can, yes. HTTP is a stateless protocol, there is no guarantee that the connection will stay alive after a response, even if a keep-alive is requested. Whether or not to close the connection after sending the response is up to the server to decide, if the client does not request the connection to be closed.
How do you send subsequent GET/POST requests after it has dropped?
You have no choice but to reconnect to the server, and everything that involves (TCP handshakes, SSL/TLS handshakes, etc) before you can send the new request.
If you:
send an HTTP 1.0 request that does not explicitly state Connection: keep-alive, or receive an HTTP 1.0 response that does not explicitly state Connection: keep-alive
send an HTTP 1.1 request that explicitly states Connection: close, or receive an HTTP 1.1 response that explicitly states Connection: close
Then you must close your end of the connection after reading the response.
Even if the response indicates a keep-alive is in effect, the connection could still timeout and be closed before you send your next request on the same connection.
So, any time you want to send a new request, if the connection has already been closed previously, or you get a connection error while sending the request, close the socket, reconnect, and resend the request.
So do you have to recycle the socket and re-establish everything again like my example below?
Potentially, yes.
I am not 100% sure how browsers handle all this.
Exactly as described above.
This is discussed further in RFC 2616 Section 8 "Connections".

Websocket + SSL(WSS) client implementation in C++

I am trying to implement secure websocket connection (WSS) in c++ . I don't want to use any external library. I have the code which is working for normal (ws) connections. I want to add the support for HTTPS also. Can any body suggest me how to proceed in this.
Example: for normal ws connections:
GET /foo HTTP/1.1
Host: example.server.com
Upgrade: websocket
Connection: keep-alive, Upgrade
Sec-WebSocket-Key: dfGsdNhbXBsZSBub25REQ==
Sec-WebSocket-Version: 13.
The code which i have already is sending these type of requests, after creating normal socket and connected to server. In the same manner i want to do WSS also.
Please suggest me on how to do that.
Thanks,
VVK.