Запрос к API IBM Watson translate с помощью Boost::Asio
Имеется шаблон обращения к API переводчика IBM на curl
$ curl -X POST --user "apikey:{apikey}" \
--header "Content-Type: application/json" \
--data '{"text": ["Hello, world.", "How are you?"], "model_id":"en-es"}' \
"{url}/v3/translate?version=2018-05-01"
Пытаюсь сделать запрос в простом консольном приложении на Boost.Asio но не получаю ответа.
Url который выдается для доступа имеет вид: https://api.au-syd.language-translator.watson.cloud.ibm.com/instances/5XXXXXXX-XXXXXXXX-XXXXX
Не совсем понимаю как правильно оформить запрос.
При попытке привязки сокета на выданный url
ip::tcp::resolver::query query("https://api.au-syd.language-translator.watson.cloud.ibm.com/instances/5XXXXXXX-XXXXXXXX-XXXXX", "443");
Вызывается исключение, я так понимаю этот url надо как то использовать в запросе и пробовал разные варианты но безуспешно. Строку с api:{key} преобразую в Base64
Весь код:
#include <iostream>
#include <sstream>
#include <boost/asio.hpp>
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/transform_width.hpp>
#include <boost/archive/iterators/insert_linebreaks.hpp>
#include <boost/archive/iterators/remove_whitespace.hpp>
#include <boost/asio/ssl.hpp>
#pragma comment(lib,"libcrypto.lib")
#pragma comment(lib, "libeay32.lib")
#pragma comment(lib, "ssleay32.lib")
#pragma comment(lib, "libssl.lib")
using namespace boost::asio;
using namespace boost::archive::iterators;
int main()
{
typedef insert_linebreaks<base64_from_binary<transform_width<std::string::const_iterator, 6, 8> >, 72 > it_base64_t;
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
//create socket
io_service service;
ssl::context ctx(ssl::context::method::sslv23_client);
ssl::stream<ip::tcp::socket> sslSocket(service, ctx);
ip::tcp::resolver resolver(service);
auto it = resolver.resolve({ "api.au-syd.language-translator.watson.cloud.ibm.comt", "443" });
boost::asio::connect(sslSocket.lowest_layer(), it);
sslSocket.handshake(ssl::stream_base::handshake_type::client);
//Encode key
std::string s="apikey:W4XXXXXXXXXXXXXXXXXXXX";
unsigned int writePaddChars = (3 - s.length() % 3) % 3;
std::string base64(it_base64_t(s.begin()), it_base64_t(s.end()));
base64.append(writePaddChars, '=');
std::cout << "Base64 representation: " << base64 << std::endl;
//format request
boost::asio::streambuf request;
std::ostream request_stream(&request);
std::string data= "{'text': ['Hello, world.', 'How are you?'], 'model_id':'en-es'}\n";
request_stream << "POST /instances/56XXXXXXXXXXXXXXXXX/v3/translate?version=2018-05-01 HTTP/1.1\n"
<<"Host: api.au-syd.language-translator.watson.cloud.ibm.com\n"
<<"Authorization: Basic "<<base64<<"\n"
<<"User-Agent: curl/7.71.1\n"
<<"Accept: */*\n"
<<"Content-Type: application/json\n"
<<"Content-Lenght: "<<data.size()<<"\n\n"
<< data;
//send
write(sslSocket,request);
//recive
size_t read = 0;
const unsigned int MAX_BUF_LENGTH = 4096;
std::vector<char> buff(MAX_BUF_LENGTH);
std::string rcv;
do
{
read = sslSocket.read_some(buffer(buff));
if (read == -1) {
std::cout << "ERROR RECV\n";
}
else {
rcv.append(buff.cbegin(), buff.cbegin() + read);
}
} while (read == MAX_BUF_LENGTH);
std::cout << rcv << std::endl;
}
Исправил запрос, теперь идентичен тому что отправляет Curl. Без создания ssl соединение по прежнему сбрасывается. при попытке создать ssl сокет вызывается исключение:
Вызвано исключение по адресу 0x0F93B144 (libssl-1_1.dll) в ConsoleApplication3.exe: 0xC0000005: нарушение прав доступа при чтении по адресу 0x0000009C.
Ответы (1 шт):
Спасибо всем за помощь. По итогу получилось сделать запрос с ssl и успешно получить ответ с использованием Boost.Beast
Вот код:
#include <iostream>
#include <sstream>
#include <boost/asio.hpp>
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/transform_width.hpp>
#include <boost/archive/iterators/insert_linebreaks.hpp>
#include <boost/archive/iterators/remove_whitespace.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/error.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <cstdlib>
#include <string>
namespace beast = boost::beast; // from <boost/beast.hpp>
namespace http = beast::http; // from <boost/beast/http.hpp>
namespace net = boost::asio; // from <boost/asio.hpp>
namespace ssl = net::ssl; // from <boost/asio/ssl.hpp>
using tcp = net::ip::tcp; // from <boost/asio/ip/tcp.hpp>
#pragma comment(lib, "libcrypto.lib")
#pragma comment(lib, "libssl.lib")
int main()
{
setlocale(LC_ALL, "Russian");
typedef boost::archive::iterators::insert_linebreaks<boost::archive::iterators::base64_from_binary<boost::archive::iterators::transform_width<std::string::const_iterator, 6, 8> >, 72 > it_base64_t;
//Encode key
std::string s = "apikey:WXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
unsigned int writePaddChars = (3 - s.length() % 3) % 3;
std::string base64(it_base64_t(s.begin()), it_base64_t(s.end()));
base64.append(writePaddChars, '=');
std::cout << "Base64 representation: " << base64 << std::endl;
#pragma region beast
try
{
// The io_context is required for all I/O
net::io_context ioc;
ssl::context ctx(ssl::context::sslv23_client);
// This holds the root certificate used for verification
ctx.load_verify_file("rootca.crt");
// Verify the remote server's certificate
ctx.set_verify_mode(ssl::verify_none);
// These objects perform our I/O
tcp::resolver resolver(ioc);
beast::ssl_stream<beast::tcp_stream> stream(ioc, ctx);
// Set SNI Hostname (many hosts need this to handshake successfully)
if (!SSL_set_tlsext_host_name(stream.native_handle(),"api.au-syd.language-translator.watson.cloud.ibm.com"))
{
beast::error_code ec{ static_cast<int>(::ERR_get_error()), net::error::get_ssl_category() };
throw beast::system_error{ ec };
}
// Look up the domain name
auto const results = resolver.resolve("api.au-syd.language-translator.watson.cloud.ibm.com", "https");
// Make the connection on the IP address we get from a lookup
beast::get_lowest_layer(stream).connect(results);
// Perform the SSL handshake
stream.handshake(ssl::stream_base::client);
//format POST request
std::string data="{\"text\": [\"Hello, world.\", \"How are you?\"], \"model_id\":\"en-ru\"}";
http::request<http::string_body> req;
req.method(http::verb::post);
req.target("/instances/56XXXXXXXXXXXXXXXXXXXXX/v3/translate?version=2020-08-11");
req.version(11);
req.set(http::field::host, "api.au-syd.language-translator.watson.cloud.ibm.com");
req.set(http::field::authorization, "Basic "+base64);
req.set(http::field::user_agent, "curl/7.71.1");
req.set(http::field::accept, "*/*");
req.set(http::field::content_type, "application/json");
req.set(http::field::content_length, data.size());
req.body()=data;
req.prepare_payload();
// Send the HTTP request to the remote host
http::write(stream, req);
// This buffer is used for reading and must be persisted
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;
// Gracefully close the stream
beast::error_code ec;
stream.shutdown(ec);
if (ec == net::error::eof)
{
// Rationale:
// http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
ec = {};
}
if (ec)
throw beast::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;
#pragma endregion beast
}