Socket validation using Poco c++ libraries - c++

I'm working on an application that needs to perform network communication and decided to use the poco c++ libraries. After going through the network tutorial I can't seem to find any forms of validation on establishing a network connection.
In the following example a client tries to connect to a server using a tcp socket stream:
#include "Poco/Net/SocketAddress.h"
#include "Poco/Net/StreamSocket.h"
#include "Poco/Net/SocketStream.h"
#include "Poco/StreamCopier.h"
#include <iostream>
int main(int argc, char** argv)
{
Poco::Net::SocketAddress sa("www.appinf.com", 80);
Poco::Net::StreamSocket socket(sa);
Poco::Net::SocketStream str(socket);
str << "GET / HTTP/1.1\r\n"
"Host: www.appinf.com\r\n"
"\r\n";
str.flush();
Poco::StreamCopier::copyStream(str, std::cout);
return 0;
}
However, I couldn't find any information related to:
Error checking(what if www.appinf.com is unavailable or doesn't exist for that matter)
The type of exception these calls may raise
The only mention is that a SocketStream may hang if the receive timeout is not set for the socket when using formated inputs.
How can I check if a host is alive and may set up a tcp connection, implement a method such as:
void TCPClient::connectTo(std::string host, bool& connected, unsigned int port) {
std::string hi = "hi";
Poco::Net::SocketAddress clientSocketAddress(host, port);
Poco::Net::StreamSocket clientStreamSocket;
// try to connect and avoid hang by setting a timeout
clientStreamSocket.connect(clientSocketAddress, timeout);
// check if the connection has failed or not,
// set the connected parameter accordingly
// additionally try to send bytes over this connection
Poco::Net::SocketStream clientSocketStream(clientStreamSocket);
clientSocketStream << hi << std::endl;
clientSocketStream.flush();
// close the socket stream
clientSocketStream.close();
// close stream
clientStreamSocket.shutdown();
}

Related

Boost 1.67 - beast::websocket Error: resolve: Host not found (authoritative)

I am trying to use beast::websocket for my Raspberry pi project where Rpi is an IoT websocket client and Microsoft Azure WebPubSub is server-side.
I am new to C++ and Unix system. Sadly, I was unable to find that many useful reference projects using the C++ Websocket.
The first thing I wanted to do was to use the beast::websocket client example to successfully connect to a test websocket server.
Below is the example I used. beast::websocket example 1.67
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <cstdlib>
#include <iostream>
#include <string>
using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
namespace websocket = boost::beast::websocket; // from <boost/beast/websocket.hpp>
// Sends a WebSocket message and prints the response
int main(int argc, char** argv)
{
try
{
// Check command line arguments.
if(argc != 4)
{
std::cerr <<
"Usage: websocket-client-sync <host> <port> <text>\n" <<
"Example:\n" <<
" websocket-client-sync echo.websocket.org 80 \"Hello, world!\"\n";
return EXIT_FAILURE;
}
auto const host = argv[1];
auto const port = argv[2];
auto const text = argv[3];
// The io_context is required for all I/O
boost::asio::io_context ioc;
// These objects perform our I/O
tcp::resolver resolver{ioc};
websocket::stream<tcp::socket> ws{ioc};
// Look up the domain name
auto const results = resolver.resolve(host, port);
// Make the connection on the IP address we get from a lookup
boost::asio::connect(ws.next_layer(), results.begin(), results.end());
// Perform the websocket handshake
ws.handshake(host, "/");
// Send the message
ws.write(boost::asio::buffer(std::string(text)));
// This buffer will hold the incoming message
boost::beast::multi_buffer buffer;
// Read a message into our buffer
ws.read(buffer);
// Close the WebSocket connection
ws.close(websocket::close_code::normal);
// If we get here then the connection is closed gracefully
// The buffers() function helps print a ConstBufferSequence
std::cout << boost::beast::buffers(buffer.data()) << std::endl;
}
catch(std::exception const& e)
{
std::cerr << "Error: " << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Because echo.websocket.org is closed now. I used different test servers.
These are the command line for two different websocket test servers.
sudo websocket-client-sync streamer.finance.yahoo.com 80 "Hello, world!"
Returned with Error: The WebSocket stream was gracefully closed at both endpoints
sudo websocket-client-sync demo.piesocket.com/v3/channel_1?api_key=oCdCMcMPQpbvNjUIzqtvF1d2X2okWpDQj4AwARJuAgtjhzKxVEjQU6IdCjwm&notify_self 80 "Hello, world!"
Returned with Error: resolve: Host not found (authoritative)
I was able to connect both URLs with a websocket test client.
But unable to connect or keep connected through this beast example.
Especially, for the second server with an API key and forward slashes, I wasn't even able to find a host.
I thought this could be something to do with those special characters not recognized as literal?
So so far these are what I have tried.
I tried replacing / with /.
I specified the host in the cpp file
(ex. auto const host = "demo.piesocket.com/v3/channel_1?api_key=oCdCMcMPQpbvNjUIzqtvF1d2X2okWpDQj4AwARJuAgtjhzKxVEjQU6IdCjwm&notify_self";)
Used encoded URL
(ex. demo.piesocket.com%2Fv3%2Fchannel_1%3Fapi_key%3DoCdCMcMPQpbvNjUIzqtvF1d2X2okWpDQj4AwARJuAgtjhzKxVEjQU6IdCjwm%26notify_self)
used port 443 instead of 80
tried secure beast::websocket example over SSL for wss servers.
Nothing worked...
In the end, I need to use an Azure websocket URL with an access token which looks like this
wss://lupo.webpubsub.azure.com/client/hubs/Hub?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ3c3M6Ly9sdXBvLndlYnB1YnN1Yi5henVyZS5jb20vY2xpZW50L2h1YnMvSHViIiwiaWF0IjoxNjMzNTc3ODA1LCJleHAiOjE2MzM1ODE0MDV9.1xGRvCsyc1QDTBWJ01PcTarx0judpa6ZuQ8
Because this is the secure websocket, I used this example
Successfully compiled with sudo g++ -v websocket_client_sync_ssl.o -o wsstest -lpthread -lboost_system -lcrypto -lssl
But no hope. Same result.. Error: resolve: Host not found (authoritative)
I feel like I am missing something pretty simple here. Could you help me out?!
Thanks!

How can I solve the "empty response" error using nghttp2?

I'm using nghttp2_asio. I compiled it using ./configure --enable-asio-lib. Then, I added /usr/local/lib to /etc/ld.so.conf file. The code is as follows:
#include "bits/stdc++.h"
#include "nghttp2/asio_http2_server.h"
using namespace std;
using namespace nghttp2::asio_http2;
using namespace nghttp2::asio_http2::server;
int main(int argc, char **argv) {
http2 srv;
srv.num_threads(4);
srv.handle("/", [](const request &req, const response &res) {
cout << req.uri().path << endl;
header_map headers;
headers.emplace("content-type", header_value{ "text/html", false });
res.write_head(200, headers);
res.end(file_generator("index.html"));
});
boost::system::error_code ec;
if (srv.listen_and_serve(ec, "localhost", "8080")) cerr << ec.message() << endl;
return 0;
}
When I try to open the browser (Chrome or Firefox) on http://localhost:8080, it give me the following error:
This page isn't working
localhost didn't send any data.
ERR_EMPTY_RESPONSE
Even if I try with curl, it gives me the error:
curl: (52) Empty reply from server
The only thing that works is curl http://localhost:8080 --http2-prior-knowledge.
Is there a solution for this?
It looks like your browser refuses to do HTTP/2 over an unencrypted connection. The Wikipedia page has the following to say:
Although the standard itself does not require usage of encryption,[51] all major client implementations (Firefox,[52] Chrome, Safari, Opera, IE, Edge) have stated that they will only support HTTP/2 over TLS, which makes encryption de facto mandatory.[53]
cURL has a different problem: it defaults to HTTP/1 which your HTTP/2 server does not understand. Adding the flag makes it use the HTTP/2 binary protocol directly. Alternatively, connecting to an HTTPS endpoint will automatically turn on HTTP/2.
See the libnghttp2_asio documentation for an example on how to serve with encryption:
int main(int argc, char *argv[]) {
boost::system::error_code ec;
boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23);
tls.use_private_key_file("server.key", boost::asio::ssl::context::pem);
tls.use_certificate_chain_file("server.crt");
configure_tls_context_easy(ec, tls);
http2 server;
// add server handlers here
if (server.listen_and_serve(ec, tls, "localhost", "3000")) {
std::cerr << "error: " << ec.message() << std::endl;
}
}

Handling TCP/IP Packet Splitting using Qt or Similar Libraries?

I am trying to send TCP messages over my local network that exceed the MTU limit of ~1500 bytes. I know that TCP protocol splits messages exceeding 1500 bytes into individual packets at some level, but am unclear if it's something I need to deal with at the application level. So I wrote a test application in ROS and Qt, C++, to test the behavior. I picked these because they are what my overall project is written in.
My test_fragmentation_test node sets up a server, then the test_client node establishes a connection and sends a message exceeding 1500 bytes. The goal is to get the server to receive this full message as one cohesive unit.
When I test these nodes on the same computer, the server receives the full messages as one unit. However, when I put the server and client on separate computers (still same network), the server receives the message as multiple packets. The packets are either 1500 bytes, or multiples thereof, so I believe they are sometimes getting squashed together in the read buffer in pairs of two or three.
Here is my code:
tcp_fragmentation_test.cpp
#include "ros/ros.h"
#include <QtCore/QCoreApplication>
#include <QTcpSocket>
#include <QTcpServer>
#include <QDebug>
//prototype functions
void initialConnect();
void receiveMessage();
//global(ish) variables
extern QTcpServer* subserver;
extern QTcpSocket* subsocket;
QTcpServer* subserver;
QTcpSocket* subsocket;
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
//initiate ROS
ros::init(argc, argv, "tcp_fragmentation_test_node");
ros::NodeHandle n;
subserver = new QTcpServer();
//call initialConnect on first connection attempt
QObject::connect(subserver, &QTcpServer::newConnection,initialConnect);
quint16 server_port = 9998;
//start listening
QHostAddress server_IP = QHostAddress("127.0.0.1");
//start listening for incoming C2 connections
if(!subserver->listen(server_IP, server_port))
{
qDebug().noquote() << "subserver failed to start on: " + server_IP.toString() + "/" + QString::number(server_port);
}
else
{
qDebug().noquote() << "subserver started on: " + server_IP.toString() + "/" + QString::number(server_port);
}
return app.exec(); //inifite loop that starts qt event listener
}
//accept incoming connection
void initialConnect()
{
//accept the incoming connection
subsocket = subserver->nextPendingConnection();
QObject::connect(subsocket, &QTcpSocket::readyRead, receiveMessage);
}
void receiveMessage()
{
//read data in
QByteArray received_message = subsocket->readAll();
qDebug() << "message received: ";
qDebug() << received_message;
}
test_client.cpp
#include "ros/ros.h"
#include <QtCore/QCoreApplication>
#include <QTcpSocket>
#include <QTcpServer>
#include <QDebug>
//function prototypes
bool initialConnect(QString server_IP, quint16 server_port);
void sendMessage(QByteArray message);
//global(ish) variables
QTcpSocket* test_socket;
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
ros::init(argc, argv, "test_client");
ros::NodeHandle n;
initialConnect("127.0.0.1",9998);
//send 1500+ byte message, to test IP fragmentation
sendMessage("<message large than 1500 bytes>");
return app.exec(); //inifite loop that starts qt event listener
}
bool initialConnect(QString server_IP, quint16 server_port)
{
//create test_socket
test_socket = new QTcpSocket();
//try to connect, then wait a bit to make sure it was successful
test_socket->connectToHost(server_IP, server_port);
if(test_socket->waitForConnected(1000))
{
return true;
}
else
{
qDebug() << "Failed to connect to server";
return false;
}
}
//send test message
void sendMessage(QByteArray message)
{
//send message
test_socket->write(message);
test_socket->waitForBytesWritten(1000);
return;
}
I talked to one of my colleagues about this, and they said that they had not run into this issue when sending messages that exceed the MTU in both Tcl scripting language, and HTML websockets. They sent me their code to check out, but unfortunately it is a small part of a really large and not well documented codebase, so I am having parsing out what they did and why they did not run into the same issues as me.
I know that I could probably avoid this issue by including identifying headers for each message, that include the length, and then combine read messages into an overall buffer until the correct number of bytes have been read. However, I'm trying to see if there's a simpler way than this, especially after my colleague telling me that he never ran into this issue. It seems like something that should be handled behind the scenes by the TCP protocol, and I'm trying to avoid dealing with it at the application level if at all possible.
Any ideas?

Sending commands to server using Poco library

I'm new to socket programming. I'm working with the Poco library. I found this example online. (https://pocoproject.org/slides/200-Network.pdf)
#include "Poco/Net/SocketAddress.h"
#include "Poco/Net/StreamSocket.h"
#include "Poco/Net/SocketStream.h"
#include "Poco/StreamCopier.h"
#include <iostream>
int main(int argc, char** argv)
{
Poco::Net::SocketAddress sa("www.appinf.com", 80);
Poco::Net::StreamSocket socket(sa)
Poco::Net::SocketStream str(socket);
str << "GET / HTTP/1.1\r\n"
"Host: www.appinf.com\r\n"
"\r\n";
str.flush();
Poco::StreamCopier::copyStream(str, std::cout);
return 0;
}
I understand that a socket stream is created.
I cannot understand the commands. What does the "/" do after "GET" or what is "1.1". Please do explain what that particular line means.
This code does give me an output. But how do the commands work? And is there a way to give the commands from the console? Thanks.
I'm not sure what you want to do here.
Are you trying to do HTTP or not?
If not, then do your own text, but don't use port 80 as that is the well known http port.
If you want to just send whatever you type over a TCP socket, then you probably could use the StreamCopier to send everything from std::cin to str.

Websocketpp simple HTTP client

I use the excellent websocketpp library to provide a Websockets (and HTTP) server in a C++ application. I also need a HTTP client in the same app to connect to REST APIs. I have been attempting this in websocketpp also, but so far I have had little success. The following preliminary attempt gives me this log output:
[2015-03-06 18:01:18] [connect] Successful connection
[2015-03-06 18:01:18] [error] Server handshake response error: websocketpp.processor:20 (Invalid HTTP status.)
[2015-03-06 18:01:18] [disconnect] Failed: Invalid HTTP status.
This suggests my http_ handler method may need something more. Any advice would be appreciated. The websocketpp docs and examples don't seem to include a simple HTTP client.
#define _WEBSOCKETPP_CPP11_STL_
#include <websocketpp/config/asio_client.hpp>
#include <websocketpp/client.hpp>
#include <websocketpp/common/thread.hpp>
namespace {
using namespace websocketpp;
typedef client<websocketpp::config::asio_client> client;
class Client {
public:
Client(void){
client_.init_asio();
client_.set_http_handler(bind(&Client::http_,this,_1));
}
std::string get(const std::string& url) {
websocketpp::lib::error_code error;
client::connection_ptr con = client_.get_connection(url,error);
if(error) std::runtime_error("Unable to connnect.\n url: "+url+"\n message: "+error.message());
client_.connect(con);
websocketpp::lib::thread asio_thread(&client::run, &client_);
asio_thread.join();
return data_;
}
private:
void http_(connection_hdl hdl){
std::cout<<"Connected\n";
data_ = "http payload";
}
client client_;
std::string data_;
};
}
int main(void){
Client client;
client.get("http://google.com/");
}
WebSocket++'s HTTP handling features are a convenience feature designed to allow WebSocket servers to serve HTTP responses in a limited capacity. WebSocket++ is not intended for use as a generic HTTP library and does not contain the ability to play the role of a (non-WebSocket) HTTP client.
Using a separate library (such as cpp-netlib) for HTTP client functionality is a good solution.
If you're trying to do both WebSocket and HTTP in C++ there's a great library called Beast that has BOTH of these things! Its open source and builds on Boost.Asio:
https://github.com/vinniefalco/Beast/
Here's some example code:
Use HTTP to request the root page from a website and print the response:
#include <beast/http.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <string>
int main()
{
// Normal boost::asio setup
std::string const host = "boost.org";
boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r(ios);
boost::asio::ip::tcp::socket sock(ios);
boost::asio::connect(sock,
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
// Send HTTP request using beast
beast::http::request_v1<beast::http::empty_body> req;
req.method = "GET";
req.url = "/";
req.version = 11;
req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port()));
req.headers.replace("User-Agent", "Beast");
beast::http::prepare(req);
beast::http::write(sock, req);
// Receive and print HTTP response using beast
beast::streambuf sb;
beast::http::response_v1<beast::http::streambuf_body> resp;
beast::http::read(sock, sb, resp);
std::cout << resp;
}
Establish a WebSocket connection, send a message and receive the reply:
#include <beast/to_string.hpp>
#include <beast/websocket.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <string>
int main()
{
// Normal boost::asio setup
std::string const host = "echo.websocket.org";
boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r(ios);
boost::asio::ip::tcp::socket sock(ios);
boost::asio::connect(sock,
r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"}));
// WebSocket connect and send message using beast
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws(sock);
ws.handshake(host, "/");
ws.write(boost::asio::buffer("Hello, world!"));
// Receive WebSocket message, print and close using beast
beast::streambuf sb;
beast::websocket::opcode op;
ws.read(op, sb);
ws.close(beast::websocket::close_code::normal);
std::cout << to_string(sb.data()) << "\n";
}
I did not know how to prevent the websocketpp client from asking for a Upgrade: connection so I ended up using cpp-netlib for a HTTP client instead.