using boost asio to write to carbon gives broken pipe - c++

I have Grafana and Graphite running on localhost. Everything is setup as default, so the plaintext protocol is configured for port 2003, as described here
The following works as desired:
export SERVER=localhost
export PORT=2003
echo "no_cluster.fake_xen.sample 25 1488542618" | nc ${SERVER} ${PORT}
Gives me the datapoint I expect (adjust timestamp as needed).
The following minimal compileable example:
#include <chrono>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <boost/asio.hpp>
namespace basio = boost::asio;
void post_to_carbon (std::string message)
{
using btcp = boost::asio::ip::tcp;
constexpr const char* carbon_port="2003";
basio::io_service ios;
btcp::resolver resolver(ios);
btcp::resolver::query query("localhost", carbon_port);
btcp::endpoint carbon_endpoint = *resolver.resolve(query);
btcp::socket sock(ios,carbon_endpoint);
boost::system::error_code ignored_error;
basio::write(sock, basio::buffer(message), ignored_error);
std::cout << "posting: " << message << " gave: " << ignored_error.message() << "\n";
}
int main() {
post_to_carbon("no_cluster.fake_xen.sample 25 1488542800");
}
Fails with the error message:
posting: no_cluster.fake_xen.sample 25 1488542800 gave: Broken pipe
Can someone tell me what I'm doing wrong?

The constructor form socket(io_service, endpoint) binds the local endpoint of the socket to the given endpoint.
I think what you want to do is:
btcp::socket sock(ios);
sock.connect(carbon_endpoint /* , error_code */);

Related

Poco failing to send mail

I have installed the Poco C++ library (Release 1.12.4) with vcpkg on my computer and I'm trying to send a simple Mail by using the sample delivered on the Github Page.`The script is compile with Visual Studio 17.
The full code:
#include "Poco/Net/MailMessage.h"
#include "Poco/Net/MailRecipient.h"
#include "Poco/Net/SecureSMTPClientSession.h"
#include "Poco/Net/StringPartSource.h"
#include "Poco/Net/SSLManager.h"
#include "Poco/Net/ConsoleCertificateHandler.h"
#include "Poco/Net/PrivateKeyPassphraseHandler.h"
#include "Poco/SharedPtr.h"
#include "Poco/Path.h"
#include "Poco/Exception.h"
#include <iostream>
using Poco::Net::MailMessage;
using Poco::Net::MailRecipient;
using Poco::Net::SMTPClientSession;
using Poco::Net::SecureSMTPClientSession;
using Poco::Net::StringPartSource;
using Poco::Net::SSLManager;
using Poco::Net::Context;
using Poco::Net::InvalidCertificateHandler;
using Poco::Net::ConsoleCertificateHandler;
using Poco::SharedPtr;
using Poco::Path;
using Poco::Exception;
using Poco::UInt16;
using namespace std;
class SSLInitializer
{
public:
SSLInitializer()
{
Poco::Net::initializeSSL();
}
~SSLInitializer()
{
Poco::Net::uninitializeSSL();
}
};
int main()
{
SSLInitializer sslInitializer;
std::string mailhost("smtp.office365.com");
std::string recipient("recipient#gmail.com");
std::string username("MyMail#gmail.com");
std::string password("MyPassword");
Poco:UInt16 port = 587;
try
{
// Note: we must create the passphrase handler prior Context
SharedPtr<InvalidCertificateHandler> pCert = new ConsoleCertificateHandler(false); // ask the user via console
Context::Ptr pContext = new Context(Context::CLIENT_USE, "");
SSLManager::instance().initializeClient(0, pCert, pContext);
MailMessage message;
message.addRecipient(MailRecipient(MailRecipient::PRIMARY_RECIPIENT, recipient));
message.setSubject("Hello from the POCO C++ Libraries");
std::string content;
content += "Hello ";
content += recipient;
content += ",\r\n\r\n";
content += "This is a greeting from the POCO C++ Libraries.\r\n\r\n";
message.addContent(new StringPartSource(content));
SecureSMTPClientSession session(mailhost, port);
session.login();
session.startTLS(pContext);
if (!username.empty())
{
session.login(SMTPClientSession::AUTH_LOGIN, username, password);
}
session.sendMessage(message);
session.close();
}
catch (Exception& exc)
{
std::cerr << exc.displayText() << std::endl;
return 1;
}
return 0;
}
I already tried the code on my first computer and it works. I wanted to try it on my laptop but it raises following error:
Poco::Net::NoCertificateException
and after a while:
SSL Exception: Error during handshake: failed to read data
I located the error and it comes from this line:
session.startTLS(pContext);
Thank you for helping me!
P.S: I don't use OpenSSL but NetSSL_Win, an implementation of the POCO NetSSL library based on Windows Schannel.

How do I write a minimal GraphQL query of the Axie server?

I'm writing a program to download data about Axies and process them. My plan is to download all the marketplace, getting just the index numbers, then download details about Axies. Before getting all the details about an Axie, I'd like to get just one detail. I've succeeded in making an HTTPS connection to the server and sending a query, but all it replies is "Bad Request".
I've been using Shane Maglangit's site https://axie-graphql.web.app/ for examples, but the examples are too big for me to understand, since I don't know GraphQL or JSON, and part of the queries has literal \n and the other part has linefeeds, which is confusing me. His code is in JavaScript, which I don't know, so I don't know if JS is doing something different with \n than C++ does.
Here's my code:
main.cpp
#include <iostream>
#include <iomanip>
#include <boost/program_options.hpp>
#include "http.h"
using namespace std;
int main(int argc,char **argv)
{
string query="{\n \"operationName\": \"GetAxieDetail\",\n"
" \"variables\":\n {\n \"axieId\": \"5144540\"\n },\n"
" \"query\": \"query GetAxieDetail($axieId: ID!) {\\n ...AxieDetail\\n __typename}\n}"
"fragment AxieDetail on Axie{axie(axieId: $axieId)}\"";
string response;
string urlv2="https://axieinfinity.com/graphql-server-v2/graphql";
string urlv1="https://graphql-gateway.axieinfinity.com/graphql";
response=httpPost(urlv1,query);
cout<<response<<endl;
return 0;
}
http.h
#include <string>
std::string httpPost(std::string url,std::string data);
http.cpp
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/asio/ssl/error.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <array>
#include <iostream>
namespace beast=boost::beast;
namespace http=beast::http;
namespace net=boost::asio;
namespace ssl=net::ssl;
using tcp=net::ip::tcp;
using namespace std;
array<string,4> parseUrl(string url)
// protocol, hostname, port, path. All are strings, including the port.
{
size_t pos0=url.find("://");
size_t pos1;
array<string,4> ret;
ret[0]=url.substr(0,pos0);
if (pos0<url.length())
pos0+=3;
pos1=url.find("/",pos0);
ret[1]=url.substr(pos0,pos1-pos0);
ret[3]=url.substr(pos1);
pos0=ret[1].find(":");
if (pos0<ret[1].length())
{
ret[2]=ret[1].substr(pos0+1);
ret[1]=ret[1].substr(0,pos0);
}
else
if (ret[0]=="https")
ret[2]="443";
else if (ret[0]=="https")
ret[2]="80";
else
ret[2]="0";
return ret;
}
string httpPost(string url,string data)
{
net::io_context context;
ssl::context ctx(ssl::context::tlsv12_client);
tcp::resolver res(context);
tcp::resolver::results_type endpoints;
beast::ssl_stream<beast::tcp_stream> stream(context,ctx);
array<string,4> parsed=parseUrl(url);
http::request<http::string_body> req;
http::response<http::string_body> resp;
beast::flat_buffer buffer;
//load_root_certificates(ctx);
ctx.set_verify_mode(ssl::verify_peer);
endpoints=res.resolve(parsed[1],parsed[2]);
beast::get_lowest_layer(stream).connect(endpoints);
SSL_set_tlsext_host_name(stream.native_handle(),parsed[1].c_str());
if (parsed[0]=="https")
stream.handshake(net::ssl::stream_base::client);
req.method(http::verb::post);
req.target(parsed[3]);
req.set(http::field::host,parsed[1]);
req.set(http::field::user_agent,BOOST_BEAST_VERSION_STRING);
req.set(http::field::content_type,"application/json");
req.set(http::field::accept,"application/json");
req.body()=data;
req.prepare_payload();
http::write(stream,req);
http::read(stream,buffer,resp);
cout<<parsed[0]<<"|\n"<<parsed[1]<<"|\n"<<parsed[2]<<"|\n"<<parsed[3]<<"|\n";
cout<<data<<"|\n";
return resp.body();
}
How can I write a query that returns one detail of the Axie with the specified number? Which of the two Axie servers should I use, and what's the difference?
Here is a working query string:
string query="{\n"
" \"operationName\": \"GetAxieDetail\",\n"
" \"variables\":\n"
" {\n"
" \"axieId\": \"5144540\"\n"
" },\n"
" \"query\":\n"
" \"query GetAxieDetail($axieId: ID!)"
" {\\n"
" axie(axieId: $axieId)\\n"
" {\\n"
" class\\n"
" }\\n"
" }\"\n"
"}\n";
The response is:
{"data":{"axie":{"class":"Plant"}}}
The server insists on no line feeds in the quoted query string, but allows \n; the \n, though, makes no difference, as the response is just one line.

Is this code correct to send logs to remote syslog server using log4cplus?

Can someone please tell me if this is correct way of using SysLogAppender of log4cplus ? I did not find a proper example for log4cplus. I need to send numerous logs to remote syslog server.
main.cpp
int main()
{
SysLogHelper syslogHelper;
int errCode = syslogHelper.initialize("172.16.72.239");
errCode = syslogHelper.sendLogstoSyslog("send testing log");
// I need to send numerous logs to syslog
}
syslog.cpp
#include <log4cplus/syslogappender.h>
#include <log4cplus/spi/loggingevent.h>
#include <log4cplus/logger.h>
class SysLogHelper
{
string hostname;
log4cplus::SysLogAppender *syslogAppender;
// is it necessary to create a pointer? I am not able to use log4cplus in a class without creating a pointer? Is there any other way?
log4cplus::spi::InternalLoggingEvent syslogLoggingEvent;
public:
SysLogHelper();
int initialize(string hostname);
int sendLogstoSyslog(string message);
};
SysLogHelper::SysLogHelper()
{
hostname = "";
syslogAppender = NULL;
}
int SysLogHelper::initialize(string hostname)
{
syslogAppender = new log4cplus::SysLogAppender("ident", hostname);
//I am not getting what is "ident" here? what input is expected?
return 0;
}
int SysLogHelper::sendLogstoSyslog(string message)
{
syslogLoggingEvent.setLoggingEvent(
log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("test")).getName(),
log4cplus::FATAL_LOG_LEVEL, LOG4CPLUS_TEXT(message),__FILE__,
__LINE__, "main");
syslogAppender->doAppend(syslogLoggingEvent);
//Is this correct method of sending logs to syslog?
return 0;
}
questions:
I am able to send logs to remote syslog using above code. But is this correct way to use log4cplus APIs? Questions are given in the form of comments in above code example.
Why do we need to use log4cplus::initializer? I am not able to import log4cplus/initializer.h in my code.
In my opinion the phylosophy at the base of Log4cplus library is that you can have in your application one or more logger and for each logger you can have one or more output, called "appender". Inside your application you have to manage with logger and you don't have care which appender are linked to the logger. This is clear, for example, if you use a property file to config and to tune your logger.
By the way here in the following I show what I have done to configure syslog inside my application in both cases:
configuring syslog appender inside the code:
#include <log4cplus/logger.h>
#include <log4cplus/fileappender.h>
#include <log4cplus/syslogappender.h>
#include <log4cplus/layout.h>
#include <log4cplus/ndc.h>
#include <log4cplus/helpers/loglog.h>
#include <log4cplus/helpers/property.h>
#include <log4cplus/loggingmacros.h>
#include <log4cplus/configurator.h>
#include <log4cplus/helpers/stringhelper.h>
#include <log4cplus/helpers/fileinfo.h>
#include <TCHAR.h>
using namespace std;
using namespace log4cplus;
using namespace log4cplus::helpers;
using namespace log4cplus;
int main()
{
log4cplus::initialize ();
Logger root = Logger::getRoot();
// log level INFO: you don't see TRACE and DEBUG on your syslogserver
root.setLogLevel(log4cplus::INFO_LOG_LEVEL);
SharedObjectPtr<Appender> ptrSys(
new SysLogAppender(
_T("mysyslog"),
_T("localhost"),
514,
_T("user"))) ;
root.addAppender(ptrSys);
for(int i=0; i<100; ++i)
{
LOG4CPLUS_TRACE(root, LOG4CPLUS_TEXT("Error log test")); //not visible
LOG4CPLUS_DEBUG(root, LOG4CPLUS_TEXT("Debug log test")); //not visible
LOG4CPLUS_INFO(root, LOG4CPLUS_TEXT("Info log test"));
LOG4CPLUS_WARN(root, LOG4CPLUS_TEXT("Warning log test"));
LOG4CPLUS_ERROR(root, LOG4CPLUS_TEXT("Error log test"));
}
log4cplus::Logger::shutdown();
return 0;
}
As I said the way, that I prefer, is by configuration file, called here configlog.properties
#include <log4cplus/logger.h>
#include <log4cplus/fileappender.h>
#include <log4cplus/syslogappender.h>
#include <log4cplus/layout.h>
#include <log4cplus/ndc.h>
#include <log4cplus/helpers/loglog.h>
#include <log4cplus/helpers/property.h>
#include <log4cplus/loggingmacros.h>
#include <log4cplus/configurator.h>
#include <log4cplus/helpers/stringhelper.h>
#include <log4cplus/helpers/fileinfo.h>
#include <TCHAR.h>
using namespace std;
using namespace log4cplus;
using namespace log4cplus::helpers;
using namespace log4cplus;
log4cplus::tstring getPropertiesFileArgument (std::wstring argv)
{
log4cplus::tstring file = LOG4CPLUS_C_STR_TO_TSTRING (argv);
log4cplus::helpers::FileInfo fi;
if (getFileInfo (&fi, file) == 0)
return file;
return LOG4CPLUS_TEXT ("log4cplus.properties");
}
int main()
{
log4cplus::initialize ();
PropertyConfigurator::doConfigure( getPropertiesFileArgument(_T("c:\\ConfigLog.properties")));
Logger root = Logger::getRoot();
for(int i=0; i<100; ++i) {
LOG4CPLUS_TRACE(root, LOG4CPLUS_TEXT("Error log test"));
LOG4CPLUS_DEBUG(root, LOG4CPLUS_TEXT("Debug log test"));
LOG4CPLUS_INFO(root, LOG4CPLUS_TEXT("Info log test"));
LOG4CPLUS_WARN(root, LOG4CPLUS_TEXT("Warning log test"));
LOG4CPLUS_ERROR(root, LOG4CPLUS_TEXT("Error log test"));
}
log4cplus::Logger::shutdown();
return 0;
}
the configlog.properties file is something like this
log4cplus.rootLogger=INFO, syslog
log4cplus.appender.syslog=log4cplus::SysLogAppender
log4cplus.appender.syslog.ident=syslog
log4cplus.appender.syslog.layout=log4cplus::PatternLayout
log4cplus.appender.syslog.layout.ConversionPattern=[%T] %-5p %b %x - %m%n
log4cplus.appender.syslog.host=localhost
log4cplus.appender.syslog.udp=true
log4cplus.appender.syslog.port=514
log4cplus.appender.syslog.facility=user
I hope to be not too in late and I hope this can be helpful for you

POCO C++ Library, HTTPResponse bad status

I am trying to unzip a file from a network stream using POCO C++ library on Ubuntu Linux, but decompressing fails with "Illegal state" exception. HTTPResponse status and reason are 302 Moved Temporarily. At the same time i can download and unzip the link with a browser. What should i do with HTTPClientSession when HTTPResponse is in such state?
...
HTTPResponse res;
std::istream& rs = h_ses.receiveResponse (res);
if (res.getStatus () != HTTPResponse::HTTP_OK) {
poco_error (logger, "http response status: " + std::to_string (res.getStatus ()) + " " + res.getReason ());
}
if (!rs) {
poco_error (logger, "responese stream is in bad state: " + std::to_string (rs.rdstate()));
}
Decompress dec (rs, target_dir_.native ());
poco_debug (logger, "Unzipping: " + dl + " ...");
dec.EError += Poco::Delegate<Addon_Loader, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &Addon_Loader::on_decompress_error);
dec.decompressAllFiles ();
...
You can do it with HTTPStreamFactory; here's a full example:
#include "Poco/URI.h"
#include "Poco/URIStreamOpener.h"
#include "Poco/Net/HTTPStreamFactory.h"
#include "Poco/StreamCopier.h"
#include <iostream>
#include <memory>
using namespace Poco;
using namespace Poco::Net;
int main()
{
URIStreamOpener opener;
opener.registerStreamFactory("http", new HTTPStreamFactory);
URI uri("http://httpbin.org/redirect-to?url=http%3A%2F%2Fexample.com%2F");
std::auto_ptr<std::istream> pStr(opener.open(uri));
StreamCopier::copyStream(*pStr.get(), std::cout);
return 0;
}
If you have to use HTTPClientSession, look at how HTTPStreamFactory does it.
In the 302 response, there should be a header field which points out the new location. You simply have to follow that link instead.
See e.g. this link, or this Wikipedia page, and of course the actual HTTP 1.1 RFC.

How to run commandline/terminal utils with Boost.Process 0.5?

I foud out there is a new Boost.Process 0.5 but I cant see how to execute across Windows Linux and Mac ping or echo.
I got it working at leaast on Windows with simple:
#include <string>
#include <iostream>
#include <boost/asio.hpp>
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/process.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/system/error_code.hpp>
namespace bp = boost::process;
namespace bpi = boost::process::initializers;
namespace bio = boost::iostreams;
int main()
{
bp::pipe p = bp::create_pipe();
{
bio::file_descriptor_sink sink(p.sink, bio::close_handle);
boost::filesystem::path p("C:/Windows/System32/cmd.exe");
boost::system::error_code ec;
bp::execute(
bpi::run_exe(p),
bpi::set_cmd_line(L"cmd /c echo --echo-stderr hello"),
bpi::bind_stdout(sink),
bpi::set_on_error(ec)
);
}
bio::file_descriptor_source source(p.source, bio::close_handle);
bio::stream<bio::file_descriptor_source> is(source);
std::string s;
is >> s;
std::cout << s << std::endl;
std::cin.get();
return 0;
}
On windows this works correctly but how to make it crossplatform to work also on Mac and Linux? (I am stupid and do not know how to write one path that would work for any Unix terminal (or at least for Linux Bash and mac default one)) So How to run commandline/terminal utils with Boost.Process 0.5 on Windows and Unix like OSs (better not writing path to terminal each time but just writting app like echo or ping and its arguments)?
...Found related code inside prevoius version:
std::string exe;
std::vector<std::string> args;
#if defined(BOOST_POSIX_API)
exe = "/bin/sh";
args.push_back("sh");
args.push_back("-c");
args.push_back(command);
#elif defined(BOOST_WINDOWS_API)
char sysdir[MAX_PATH];
UINT size = ::GetSystemDirectoryA(sysdir, sizeof(sysdir));
if (!size)
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::launch_shell: GetWindowsDirectory failed"));
BOOST_ASSERT(size < MAX_PATH);
exe = std::string(sysdir) + (sysdir[size - 1] != '\\' ? "\\cmd.exe" : "cmd.exe");
args.push_back("cmd");
args.push_back("/c");
args.push_back(command);
#endif
Under boost.process 0.5 the shell_path() API was introduced so might the following will hook you up
#if defined(BOOST_POSIX_API)
#define SHELL_COMMAND_PREFIX "-c"
#elif defined(BOOST_WINDOWS_API)
#define SHELL_COMMAND_PREFIX "/c"
#endif
filesystem::path shellPath = process::shell_path();
std::string cl = shell_path().string() + " " SHELL_COMMAND_PREFIX " ";
cl += "ping 127.0.0.1";
execute(
set_cmd_line(cl),
throw_on_error()
);
If you really want to hide the #ifdef, I'd go on and edit the boost sources to return also the relevant command prefix (adding new API) , open source after all isn't it ? :).
You could find the relevant sources to edit at boost/process/windows/shell_path.hpp and boost/process/posix/shell_path.hpp