POCO C++ Library, HTTPResponse bad status - c++

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.

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.

Multipart Upload S3 using AWS C++ SDK

I am trying to upload a file to S3 using multipart upload feature in AWS C++ SDK. I could find examples for JAVA, .NET, PHP, RUBY and Rest API, but didnt find any lead on how to do it in C++. Can you please provide me a direction to achieve the same.
transfer manager needs the file to be stored on disk and have a filename. That's not good for streaming. Here's the code template that I used to do prototyping.
#include <aws/core/Aws.h>
#include <aws/s3/S3Client.h>
#include <aws/s3/model/CreateMultipartUploadRequest.h>
#include <aws/core/utils/HashingUtils.h>
#include <aws/s3/model/CompleteMultipartUploadRequest.h>
#include <aws/s3/model/GetObjectRequest.h>
#include <aws/s3/model/UploadPartRequest.h>
#include <sstream>
#include <iostream>
#include <string>
using namespace Aws::S3::Model;
int main(int argc, char *argv[]) {
// new api
Aws::SDKOptions options;
Aws::InitAPI(options);
// use default credential provider chains
Aws::Client::ClientConfiguration clientConfiguration;
clientConfiguration.region = "<your-region>";
clientConfiguration.endpointOverride = "<endpoint-override>";
Aws::S3::S3Client s3_client(clientConfiguration);
std::string bucket = "<bucket>";
std::string key = "<key>";
// initiate the process
Aws::S3::Model::CreateMultipartUploadRequest create_request;
create_request.SetBucket(bucket.c_str());
create_request.SetKey(key.c_str());
create_request.SetContentType("text/plain");
auto createMultipartUploadOutcome =
s3_client.CreateMultipartUpload(create_request);
std::string upload_id = createMultipartUploadOutcome.GetResult().GetUploadId();
std::cout << "multiplarts upload id is:" << upload_id << std::endl;
// start upload
Aws::S3::Model::UploadPartRequest my_request;
my_request.SetBucket(bucket.c_str());
my_request.SetKey(key.c_str());
my_request.SetPartNumber(1);
my_request.SetUploadId(upload_id.c_str());
Aws::StringStream ss;
// just have a small chunk of data to verify everything works
ss << "to upload";
std::shared_ptr<Aws::StringStream> stream_ptr =
Aws::MakeShared<Aws::StringStream>("WriteStream::Upload" /* log id */, ss.str());
my_request.SetBody(stream_ptr);
Aws::Utils::ByteBuffer part_md5(
Aws::Utils::HashingUtils::CalculateMD5(*stream_ptr));
my_request.SetContentMD5(Aws::Utils::HashingUtils::Base64Encode(part_md5));
auto start_pos = stream_ptr->tellg();
stream_ptr->seekg(0LL, stream_ptr->end);
my_request.SetContentLength(static_cast<long>(stream_ptr->tellg()));
stream_ptr->seekg(start_pos);
auto uploadPartOutcomeCallable1 = s3_client.UploadPartCallable(my_request);
// finish upload
Aws::S3::Model::CompleteMultipartUploadRequest completeMultipartUploadRequest;
completeMultipartUploadRequest.SetBucket(bucket.c_str());
completeMultipartUploadRequest.SetKey(key.c_str());
completeMultipartUploadRequest.SetUploadId(upload_id.c_str());
UploadPartOutcome uploadPartOutcome1 = uploadPartOutcomeCallable1.get();
CompletedPart completedPart1;
completedPart1.SetPartNumber(1);
auto etag = uploadPartOutcome1.GetResult().GetETag();
// if etag must not be empty
assert(etag.empty());
completedPart1.SetETag(etag);
completeMultipartUploadRequest.SetBucket(bucket.c_str());
completeMultipartUploadRequest.SetKey(key.c_str());
completeMultipartUploadRequest.SetUploadId(upload_id.c_str());
CompletedMultipartUpload completedMultipartUpload;
completedMultipartUpload.AddParts(completedPart1);
completeMultipartUploadRequest.WithMultipartUpload(completedMultipartUpload);
auto completeMultipartUploadOutcome =
s3_client.CompleteMultipartUpload(completeMultipartUploadRequest);
if (!completeMultipartUploadOutcome.IsSuccess()) {
auto error = completeMultipartUploadOutcome.GetError();
std::stringstream ss;
ss << error << error.GetExceptionName() << ": " << error.GetMessage() << std::endl;
return -1;
}
}
I recommend using Transfer Manager in general.
But if you don’t want to for whatever reason, then you look at source of transfer manager to see how to do multipart upload using S3 APIs directly.

using boost asio to write to carbon gives broken pipe

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 */);

c++ Debug Assertion Failed on HTTP Request

I'm doing some code where i need to do a GET request and manipulate the info received. For this i'm using C++ REST SDK (codename "Casablanca") for the request
This is my code
#include <cpprest/http_client.h>
#include <cpprest/filestream.h>
using namespace utility;
using namespace web;
using namespace web::http;
using namespace web::http::client;
using namespace concurrency::streams;
//This method i saw on the Microsoft documentation
pplx::task<void> HTTPStreamingAsync()
{
http_client client(L"http://localhost:10000/Something"); //The api is running at the moment
// Make the request and asynchronously process the response.
return client.request(methods::GET).then([](http_response response)
{
// Print the status code.
std::wostringstream ss;
ss << L"Server returned returned status code " << response.status_code() << L'.' << std::endl;
std::wcout << ss.str();
// TODO: Perform actions here reading from the response stream.
auto bodyStream = response.body();
// In this example, we print the length of the response to the console.
ss.str(std::wstring());
ss << L"Content length is " << response.headers().content_length() << L" bytes." << std::endl;
std::wcout << ss.str();
});
}
void main(int argc, char **argv)
{
HTTPStreamingAsync().wait();
//...
}
And when i use debug i get error on the following line:
return client.request(methods::GET).then([](http_response response)
With debug i see that variable "client" has content, but i still receive this error:
Image with the Error Message
I google it the error, and most of the people say that it is error on the code (trying to access some parts of the memory)...
Any ideas?
This issue can happen when the cpprestsdk DLL is build with Multi-Threaded DLL /MD and the calling library is build with Multi-Threaded /MT. Since the cpprestsdk does not offer a configuration for a .lib file, you are forced to use /MD. At least that is best to my knowledge, as I haven't been able to compile cpprestsdk.lib out of the box without a bunch of linker errors.