Boost io_service.accept : Get URI called - boost

I would like to get the URI called with boost.
If http://localhost:8080/users/4 is called, I want to know the URI or just /users/4
#include <boost/bind.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
int main()
{
boost::asio::io_service io;
tcp::acceptor acceptor(io, tcp::endpoint(tcp::v4(), 8080));
tcp::socket socket(io);
acceptor.accept(socket);
// Do stuff to get URI
// ...
if (uri == "http://localhost:8080/users/4") {
std::cout << "User 4 uri called" << std::endl;
}
return 0;
}

Look to the ASIO's http server example. The connection::handle_read method parses a request, using the request_parser class instance, as result, an instance of the request structure contains an URI which is required.

Related

BOOST TLS connection establishment using the boost after exchange of data between server and client

I want to initiate the TLS connection only if the server supports the secure connection, to achieve this i have introduced the two message type between the server and client.
Client will send SECURECONNREQ msg to server after tcp connection establishment, if the server is configured to support the TLS it send SECURECONNRESP.
On receiving SECURECONNRESP the client will initiate the the TLS handshake by calling boost asynhandshake API but this API is not sending the correct packet(client hello). In the wireshark i could see it is sending the protocol version as TLS1.1 even if it configured for TLS1.2.
Note: SSL context object is prepared with TLS1.2, ciphers and related certs.
It looks like the asynhandshake will not work properly if there is some data exchange happens on the TCP link.
Is there extra step we need take on BOOST asio to make it work?
The secure connection establishment works perfectly fine with below implementation:
Initiating the TLS connection without sending any data on the TCP link.
The client will initiate the TLS after connection is established(no data transfered on this link) by calling asynhandshake, the server will call boost asynhandshake immediately after the connnection accept.
It's hard, or even impossible, to tell without seeing your actual code. But I thought it a nice challenge to write the quintessential PLAIN/TLS server/client in Asio, just to see whether there were any problems I might not have anticipated.
Sadly, it works. You may compare these and decide what you're doing differently.
If anything, this should help you create a MCVE/SSCCE to repro your issue.
Short Intro
The server accepts connections.
The client initiates a ConnectionRequest. Depending on a runtime flag
_support_tls the server responds with the SECURE capability response (or not).
The client and server upgrade to TLS accordingly.
The client and server execute a very simple protocol session that's
templated on the Stream type, because the implementations are independent of
the transport layer security.
Note, for the example I've used the server.pem from the Asio SSL examples.
The demo then executes servers both with and without SECURE capability, and
verifies that the same simple_client implementation works correctly
against both.
Listing
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/endian/arithmetic.hpp>
#include <boost/beast.hpp>
#include <boost/beast/http.hpp>
#include <iostream>
namespace http = boost::beast::http;
namespace net = boost::asio;
namespace ssl = net::ssl;
using boost::system::error_code;
using net::ip::tcp;
struct ConnectionRequest {
enum { MAGIC = 0x64fc };
boost::endian::big_uint16_t magic = MAGIC;
};
struct ConnectionResponse {
enum Capabilities {
NONE,
SECURE,
};
boost::endian::big_uint16_t capabilities = NONE;
};
using TLSStream = ssl::stream<tcp::socket>;
template <typename> static inline char const* stream_type = "(plain)";
template <> inline char const* stream_type<TLSStream> = "(TLS)";
static void do_shutdown(tcp::socket& s) {
error_code ignored;
s.shutdown(tcp::socket::shutdown_both, ignored);
}
static void do_shutdown(TLSStream& s) {
error_code ignored;
s.shutdown(ignored);
}
template <typename Stream>
struct Session : std::enable_shared_from_this<Session<Stream> > {
Session(Stream&& s) : _stream(std::move(s)) {}
void start() {
std::cout << "start" << type() << ": " << _stream.lowest_layer().remote_endpoint() << std::endl;
http::async_read(_stream, _buf, _req, self_bind(&Session::on_request));
}
private:
Stream _stream;
http::request<http::string_body> _req;
http::response<http::empty_body> _res;
net::streambuf _buf;
void on_request(error_code ec, size_t) {
std::cout << "on_request" << type() << ": " << ec.message() << std::endl;
http::async_write(_stream, _res, self_bind(&Session::on_response));
}
void on_response(error_code ec, size_t) {
std::cout << "on_reponse" << type() << ": " << ec.message() << std::endl;
do_shutdown(_stream);
}
auto type() const { return stream_type<Stream>; }
auto self_bind(auto member) {
return [self=this->shared_from_this(), member](auto&&... args) {
return (*self.*member)(std::forward<decltype(args)>(args)...);
};
}
};
struct Server {
using Ctx = ssl::context;
Server(auto executor, uint16_t port, bool support_tls)
: _acceptor(executor, {{}, port}),
_support_tls(support_tls)
{
if (_support_tls) {
_ctx.set_password_callback(
[](size_t, Ctx::password_purpose) { return "test"; });
_ctx.use_certificate_file("server.pem", Ctx::file_format::pem);
_ctx.use_private_key_file("server.pem", Ctx::file_format::pem);
}
_acceptor.listen();
accept_loop();
}
~Server() {
_acceptor.cancel();
}
private:
void accept_loop() {
_acceptor.async_accept(
make_strand(_acceptor.get_executor()),
[=, this](error_code ec, tcp::socket&& sock) {
if (!ec)
accept_loop();
else
return;
// TODO not async for brevity of sample here
ConnectionRequest req[1];
read(sock, net::buffer(req));
if (req->magic != ConnectionRequest::MAGIC) {
std::cerr << "Invalid ConnectionRequest" << std::endl;
return;
}
ConnectionResponse res[1]{{ConnectionResponse::NONE}};
if (not _support_tls) {
write(sock, net::buffer(res));
std::make_shared<Session<tcp::socket>>(std::move(sock))
->start();
} else {
res->capabilities = ConnectionResponse::SECURE;
write(sock, net::buffer(res));
// and then do the handshake
TLSStream stream(std::move(sock), _ctx);
stream.handshake(TLSStream::handshake_type::server);
std::make_shared<Session<TLSStream>>(std::move(stream))
->start();
}
});
}
tcp::acceptor _acceptor;
Ctx _ctx{Ctx::method::sslv23};
bool _support_tls;
};
// simplistic one-time request/response convo
template <typename Stream> void simple_conversation(Stream& stream) {
http::write(stream, http::request<http::string_body>(
http::verb::get, "/demo/api/v1/ping", 11,
"hello world"));
{
http::response<http::string_body> res;
net::streambuf buf;
http::read(stream, buf, res);
std::cout << "Received: " << res << std::endl;
}
}
void simple_client(auto executor, uint16_t port) {
// client also not async for brevity
tcp::socket sock(executor);
sock.connect({{}, port});
ConnectionRequest req[1];
write(sock, net::buffer(req));
ConnectionResponse res[1];
read(sock, net::buffer(res));
std::cout << "ConnectionResponse: " << res->capabilities << std::endl;
if (res->capabilities != ConnectionResponse::SECURE) {
simple_conversation(sock);
} else {
std::cout << "Server supports TLS, upgrading" << std::endl;
ssl::context ctx{ssl::context::method::sslv23};
TLSStream stream(std::move(sock), ctx);
stream.handshake(TLSStream::handshake_type::client);
simple_conversation(stream);
}
}
int main() {
net::thread_pool ctx;
{
Server plain(ctx.get_executor(), 7878, false);
simple_client(ctx.get_executor(), 7878);
}
{
Server tls(ctx.get_executor(), 7879, true);
simple_client(ctx.get_executor(), 7879);
}
ctx.join();
}
Prints
ConnectionResponse: start(plain): 0
127.0.0.1:37924
on_request(plain): Success
on_reponse(plain): Success
Received: HTTP/1.1 200 OK
ConnectionResponse: 1
Server supports TLS, upgrading
start(TLS): 127.0.0.1:36562
on_request(TLS): Success
on_reponse(TLS): Success
Received: HTTP/1.1 200 OK

UWP Server Socket not listening

I am creating a simple UWP console server app with reference to the contents of this and this links.
However, if i create code based on the examples and run it, I will not be able to connect to the server. Also, the port is not open when checked with a command such as "netstat -na".
Does anyone know how to solve it?
The code is shown below. I would appreciate if you find any errors in the code below.
#include "pch.h"
#include <sstream>
#include <iostream>
using namespace Platform;
using namespace Concurrency;
using namespace Windows::Foundation;
using namespace Windows::Networking;
using namespace Windows::Networking::Sockets;
using namespace Windows::Networking::Connectivity;
using namespace Windows::Storage::Streams;
namespace SocketTest {
public ref class Server sealed {
public:
Server();
void Run();
protected:
void Server::onConnReceived(
Windows::Networking::Sockets::StreamSocketListener^ listener,
Windows::Networking::Sockets::StreamSocketListenerConnectionReceivedEventArgs^ object);
};
}
using namespace SocketTest;
Server::Server() {
}
void Server::onConnReceived(
StreamSocketListener^ listener,
StreamSocketListenerConnectionReceivedEventArgs^ object)
{
try {
DataReader^ reader = ref new DataReader(object->Socket->InputStream);
//ReceiveStringLoop(reader, object->Socket);
delete object->Socket;
}
catch (Exception^ exception) {
std::cout << "onconnection error" << std::endl;
}
}
void Server::Run() {
StreamSocketListener^ listener = ref new StreamSocketListener();
listener->ConnectionReceived += ref new TypedEventHandler<Windows::Networking::Sockets::StreamSocketListener^, Windows::Networking::Sockets::StreamSocketListenerConnectionReceivedEventArgs^>(this, &Server::onConnReceived);
listener->Control->KeepAlive = true;
create_task(listener->BindServiceNameAsync(L"12345")).then(
[=]
{
try
{
std::cout << "Listening ..." << std::endl;
}
catch (Exception^ exception)
{
std::cout << "Bind error" << exception->Message->Data() << std::endl;
}
});
}
int main(Platform::Array<Platform::String^>^ args)
{
Server^ s = ref new Server();
s->Run();
getchar();
}
Or, if you have a better code example, let me know.

Boost Log Callback

I have created a logger mechanism based on Boost Log.
My code is based on the trivial logger as shown in this example.
I was wondering how to automatically call system exit
exit(1)
(or any other custom callback function) whenever a fatal error occurs.
Any help is welcomed!
UPDATE:
The solution is to extend the backend sink by overloading the consume() method.
Example of a sink examining the severity level of the trivial logger:
#include <boost/log/trivial.hpp>
#include <boost/log/sinks/basic_sink_backend.hpp>
#include <boost/log/attributes/value_extraction.hpp>
#include <boost/log/sinks/async_frontend.hpp>
namespace sinks = boost::log::sinks;
void initBoostLog() {
struct Sink: public sinks::basic_formatted_sink_backend<char, sinks::concurrent_feeding> {
void consume (const boost::log::record_view& rec, const string& str) {
using boost::log::trivial::severity_level;
auto severity = rec.attribute_values()[boost::log::aux::default_attribute_names::severity()].extract<severity_level>();
if (!severity || severity.get() <= severity_level::info) {
std::cout << str << std::endl;
} else {
std::cerr << str << std::endl;
}
}
};
typedef sinks::asynchronous_sink<Sink> sink_t; boost::shared_ptr<sink_t> sink (new sink_t());
boost::shared_ptr<boost::log::core> logc = boost::log::core::get();
logc->add_sink (sink);
}

Using boost::asio::io_service::post()

First i asked this Running a function on the main thread from a boost thread and passing parameters to that function
so now i am trying this:
The following is a console c++ project where i perfectly simulated my big project
TestServicePost.cpp
#include "stdafx.h"
#include "SomeClass.h"
int _tmain(int argc, _TCHAR* argv[])
{
SomeClass* s = new SomeClass();
while(true)
{
s->update();
}
return 0;
}
SomeClass.h
#include <boost/thread.hpp>
#include <boost/asio.hpp>
#include <queue>
class ServiceNote
{
public:
std::string getType()
{
std::stringstream typeSS;
typeSS << "LamasaTech.MultiWall.PostNote." << (NoteType.compare("Normal") == 0 ? "Node" : "Header") << "." << Shape << "." << Colour;
return typeSS.str();
}
int Action;
int CNoteId;
std::string Colour;
int NoteId;
std::string NoteType;
int SessionId;
std::string Shape;
std::string Style;
std::string Text;
int X;
int Y;
};
class SomeClass
{
public:
SomeClass();
~SomeClass();
void update();
private:
std::queue<ServiceNote> pendingNotes;
void addToQueue(ServiceNote sn);
void pollService(boost::asio::io_service* svc);
int getMessage(boost::asio::io_service* svc, std::string sessionId, int messageId);
boost::thread servicePoller;
};
SomeClass.cpp
#include "stdafx.h"
#include "SomeClass.h"
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/asio/signal_set.hpp>
#define POLL_SERVICE = 0;
#define POLLING_WAIT_TIME 1000
#define SAVE_SESSION_EVERY 1800000
SomeClass::SomeClass()
{
boost::asio::io_service io_servicePoller;
io_servicePoller.run();
servicePoller = boost::thread(boost::bind(&SomeClass::pollService, this, &io_servicePoller));
/*boost::asio::io_service io_sessionSaver;
boost::asio::signal_set signalsSaver(io_sessionSaver, SIGINT, SIGTERM);
signalsSaver.async_wait( boost::bind(&boost::asio::io_service::stop, &io_sessionSaver));
sessionSaver = boost::thread(&SomeClass::saveSessionEvery, io_sessionSaver);*/
}
SomeClass::~SomeClass()
{
}
void SomeClass::update()
{
while(!pendingNotes.empty())
{
ServiceNote sn = pendingNotes.front();
pendingNotes.pop();
}
}
void SomeClass::addToQueue(ServiceNote sn)
{
pendingNotes.push(sn);
}
void SomeClass::pollService(boost::asio::io_service* svc)
{
int messageId = 1;
while(true)
{
if(boost::this_thread::interruption_enabled() && boost::this_thread::interruption_requested())
return;
int currentId = messageId;
messageId = getMessage(svc, "49", messageId);
if(currentId == messageId)
boost::this_thread::sleep(boost::posix_time::milliseconds(POLLING_WAIT_TIME));
}
}
int SomeClass::getMessage(boost::asio::io_service* svc, std::string sessionId, int messageId)
{
try
{
boost::asio::io_service io_service;
// Get a list of endpoints corresponding to the server name.
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::query query("mw.rombus.com", "http");
boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
// Try each endpoint until we successfully establish a connection.
boost::asio::ip::tcp::socket socket(io_service);
boost::asio::connect(socket, endpoint_iterator);
// Form the request. We specify the "Connection: close" header so that the
// server will close the socket after transmitting the response. This will
// allow us to treat all data up until the EOF as the content.
boost::asio::streambuf request;
std::ostream request_stream(&request);
request_stream << "GET " "/Service.svc/message/" << sessionId << "/" << messageId << " HTTP/1.0\r\n";
request_stream << "Host: " << "mw.rombus.com" << "\r\n";
request_stream << "Accept: */*\r\n";
request_stream << "Connection: close\r\n\r\n";
// Send the request.
boost::asio::write(socket, request);
// Read the response status line. The response streambuf will automatically
// grow to accommodate the entire line. The growth may be limited by passing
// a maximum size to the streambuf constructor.
boost::asio::streambuf response;
boost::asio::read_until(socket, response, "\r\n");
// Check that response is OK.
std::istream response_stream(&response);
std::string http_version;
response_stream >> http_version;
unsigned int status_code;
response_stream >> status_code;
std::string status_message;
std::getline(response_stream, status_message);
if (!response_stream || http_version.substr(0, 5) != "HTTP/")
{
//std::cout << "Invalid response\n";
return messageId;
}
if (status_code != 200)
{
//std::cout << "Response returned with status code " << status_code << "\n";
return messageId;
}
// Read the response headers, which are terminated by a blank line.
boost::asio::read_until(socket, response, "\r\n\r\n");
// Process the response headers.
std::string header;
std::string fullHeader = "";
while (std::getline(response_stream, header) && header != "\r")
fullHeader.append(header).append("\n");
// Write whatever content we already have to output.
std::string fullResponse = "";
if (response.size() > 0)
{
std::stringstream ss;
ss << &response;
fullResponse = ss.str();
try
{
boost::property_tree::ptree pt;
boost::property_tree::read_json(ss, pt);
ServiceNote sn;
sn.Action = pt.get<int>("Action");
sn.CNoteId = pt.get<int>("CNoteId");
sn.Colour = pt.get<std::string>("Colour");
sn.NoteId = pt.get<int>("NoteId");
sn.NoteType = pt.get<std::string>("NoteType");
sn.SessionId = pt.get<int>("SessionId");
sn.Shape = pt.get<std::string>("Shape");
sn.Style = pt.get<std::string>("Style");
sn.Text = pt.get<std::string>("Text");
sn.X = pt.get<int>("X");
sn.Y = pt.get<int>("Y");
svc->post(boost::bind(&SomeClass::addToQueue, this, sn));
//pendingNotes.push(sn);
}
catch (std::exception const& e)
{
std::string test = e.what();
//std::cerr << e.what() << std::endl;
}
messageId++;
}
// Read until EOF, writing data to output as we go.
std::string fullSth = "";
boost::system::error_code error;
while (boost::asio::read(socket, response,
boost::asio::transfer_at_least(1), error))
{
std::ostringstream ss;
ss << &response;
fullSth = ss.str();
}
if (error != boost::asio::error::eof)
throw boost::system::system_error(error);
}
catch (std::exception& e)
{
std::string test = e.what();
std::cout << "Exception: " << e.what() << "\n";
}
return messageId;
}
but i get Unhandled exception at 0x771215de in TestServicePost.exe: 0xC0000005: Access violation writing location 0xcccccce4., right after this line executes:
svc->post(boost::bind(&SomeClass::addToQueue, this, sn));
I couldn't define io_service as a class member so i can use it in the destructor ~SomeClass(), would appreciate help on that too
If io_service.post is not the best solution for me please recommend something, as you can see i have a constructor, destructor and an update method who is called every tick, i tried using this and the queue alone but it wasn't thread safe, is there an easy thread safe FIFO to use ?
In SomeClass constructor you actually do the following:
Define a local io_service instance.
Call its run() member-function, which returns immediately, because io_service has no work.
Pass an address of the local object to another thread.
This certainly won't work.
Note that io_service::run() is a kind of "message loop", so it should block the calling thread. Don't call it in object constructor.
I figured out how to declare io_service as a class member:
boost::shared_ptr< boost::asio::io_service > io_servicePoller;
and in the constructor i did the following:
SomeClass::SomeClass()
{
boost::shared_ptr< boost::asio::io_service > io_service(
new boost::asio::io_service
);
io_servicePoller = io_service;
servicePoller = boost::thread(boost::bind(&SomeClass::pollService, this, io_servicePoller));
}
Some cleanup
SomeClass::~SomeClass()
{
servicePoller.interrupt();
io_servicePoller->stop();
servicePoller.join();
}
and in update i called run which adds the stuff into the queue, then reads them in the while loop
void SomeClass::update()
{
io_servicePoller->run();
io_servicePoller->reset();
while(!pendingNotes.empty())
{
ServiceNote sn = pendingNotes.front();
pendingNotes.pop();
}
}
and changed my members signature to void SomeClass::pollService(boost::shared_ptr< boost::asio::io_service > svc)
So what happens is:
The app starts
inits my class
my class makes a service and starts the thread
the thread fetches items from the service
the main thread checks the io service queue and exuted it
then it uses the queue
Thanks to Igor R. i couldn't have done it without him
and also http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting-started-with-boostasio?pg=4 where i got how to make the shared pointer

trying to understand gsoap

I am creating my first web service client using gsoap. I was able to the run the calc example provided with gsoap.
Now I am trying to access String GetData() function from WCF Webservice. I did the wsdl2h and soapcpp2 steps and have generated the .h file. In xxxxproxy.h I see that the prototype of GetData is as follows
/// Web service operation 'GetData' (returns error code or SOAP_OK)
virtual int GetData(_ns1__GetData *ns1__GetData, _ns1__GetDataResponse *ns1__GetDataResponse);
Can someone tell me what should I write in my main.cpp to access GetData. I have following code in main.cpp
#include <QtCore/QCoreApplication>
#include "soapWSHttpBinding_USCOREIAquaLinkProxy.h"
#include "WSHttpBinding_USCOREIAquaLink.nsmap"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
WSHttpBinding_USCOREIAquaLinkProxy webService;
std::cout <<"Sent request"<<std::endl;
std::string result;
if(webService.GetData(?????? )==SOAP_OK)
{
std::cout << "Returned "<< ????? <<std::endl;
}
else
{
webService.soap_stream_fault(std::cerr);
}
return a.exec();
}
Thanks.
The first argument _ns1__GetData in the function GetData is the request argument, the second is the response argument. You should try following:
_ns1__GetData request;
request.???? = ???? // I don't know the WCF Webservice
_ns1__GetDataResponse response;
if(webService.GetData(&request, &response) == SOAP_OK)
{
std::cout << "Returned " << response.????;
}
I don't know the WCF Webservice. But I guess that you have to fill the request instance with some values. What makes me wonder is, that the class names _ns1__GetData and _ns1__GetDataResponse begin with an underscore. I'm using gSoap for a long time and the names were always without beginning underscore.

Resources