view libmlk/malikania/socket.cpp @ 189:f28cb6d04731

Misc: extreme refactoring
author David Demelier <markand@malikania.fr>
date Thu, 25 Oct 2018 21:36:14 +0200
parents libcommon/malikania/socket.cpp@fbfc2555bda5
children 74afc5a41c83
line wrap: on
line source

/*
 * socket.cpp -- SSL socket using JSON messages
 *
 * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <cassert>

#include "socket.hpp"

namespace mlk {

void socket::do_recv(recv_handler handler)
{
	boost::asio::async_read_until(socket_, rbuffer_, "\r\n\r\n", [this, handler] (auto code, auto xfer) {
		if (code)
			handler(std::move(code), nullptr);
		else if (xfer == 0U)
			handler(make_error_code(boost::system::errc::network_down), nullptr);
		else {
			std::string str(
				boost::asio::buffers_begin(rbuffer_.data()),
				boost::asio::buffers_begin(rbuffer_.data()) + xfer - 4
			);

			// Remove early in case of errors.
			rbuffer_.consume(xfer);

			// TODO: catch nlohmann::json::parse_error when 3.0.0 is released.
			nlohmann::json message;

			try {
				message = nlohmann::json::parse(str);
			} catch (...) {}

			if (!message.is_object())
				handler(make_error_code(boost::system::errc::invalid_argument), nullptr);
			else
				handler(code, std::move(message));
		}
	});
}

void socket::do_send(const std::string& str, send_handler handler)
{
	boost::asio::async_write(socket_, boost::asio::buffer(str), [handler] (auto code, auto xfer) {
		if (xfer == 0U)
			handler(make_error_code(boost::system::errc::network_down));
		else
			handler(code);
	});
}

socket::socket(boost::asio::io_service& service,
			   boost::asio::ssl::context& ctx)
	: socket_(service, ctx)
{
}

auto socket::get_socket() const noexcept -> const socket_t&
{
	return socket_;
}

auto socket::get_socket() noexcept -> socket_t&
{
	return socket_;
}

auto socket::is_receiving() const noexcept -> bool
{
	return !rqueue_.empty();
}

auto socket::is_sending() const noexcept -> bool
{
	return !squeue_.empty();
}

auto socket::is_active() const noexcept -> bool
{
	return is_receiving() || is_sending();
}

void socket::rflush()
{
	if (rqueue_.empty())
		return;

	do_recv([this] (auto code, auto json) {
		auto handler = rqueue_.front();

		rqueue_.pop_front();
		handler(code, std::move(json));

		if (!code)
			rflush();
	});
}

void socket::sflush()
{
	if (squeue_.empty())
		return;

	do_send(squeue_.front().first, [this] (auto code) {
		auto handler = squeue_.front().second;

		squeue_.pop_front();

		if (handler)
			handler(code);
		if (!code)
			sflush();
	});
}

void socket::recv(recv_handler handler)
{
	assert(handler);

	auto in_progress = !rqueue_.empty();

	rqueue_.push_back(std::move(handler));

	if (!in_progress)
		rflush();
}

void socket::send(nlohmann::json json, send_handler handler)
{
	assert(json.is_object());

	auto in_progress = !squeue_.empty();

	squeue_.emplace_back(json.dump(0) + "\r\n\r\n", std::move(handler));

	if (!in_progress)
		sflush();
}

} // !mlk