<commit_before>#include <assert.h>
#include <string.h>

#ifdef WIN32
	#include <winsock2.h>
	#include <ws2tcpip.h>
	#define SHUT_RDWR SD_BOTH
#else // WIN32
	#include <sys/socket.h>
	#include <netinet/in.h>
	#include <netinet/tcp.h>
	#include <netdb.h>
	#include <fcntl.h>
#endif // !WIN32

#include <unordered_map>
#include <set>

#include "connection.h"

#include "utils.h"

class GlobalNetProcess {
public:
	std::mutex fd_map_mutex;
	std::unordered_map<int, Connection*> fd_map;
#ifndef WIN32
	int pipe_write;
#endif

	static void do_net_process(GlobalNetProcess* me) {
		fd_set fd_set_read, fd_set_write;
		struct timeval timeout;

#ifndef WIN32
		int pipefd[2];
		assert(!pipe(pipefd));
		fcntl(pipefd[1], F_SETFL, fcntl(pipefd[1], F_GETFL) | O_NONBLOCK);
		fcntl(pipefd[0], F_SETFL, fcntl(pipefd[0], F_GETFL) | O_NONBLOCK);
		me->pipe_write = pipefd[1];
#endif

		while (true) {
#ifndef WIN32
			timeout.tv_sec = 86400;
			timeout.tv_usec = 20 * 1000;
#else
			timeout.tv_sec = 0;
			timeout.tv_usec = 1000;
#endif

			FD_ZERO(&fd_set_read); FD_ZERO(&fd_set_write);
#ifndef WIN32
			int max = pipefd[0];
			FD_SET(pipefd[0], &fd_set_read);
#else
			int max = 0;
#endif
			auto now = std::chrono::steady_clock::now();
			{
				std::lock_guard<std::mutex> lock(me->fd_map_mutex);
				for (const auto& e : me->fd_map) {
					ALWAYS_ASSERT(e.first < FD_SETSIZE);
					if (e.second->total_inbound_size < 65536)
						FD_SET(e.first, &fd_set_read);
					if (e.second->total_waiting_size > 0) {
						FD_SET(e.first, &fd_set_write);
						if (now < e.second->earliest_next_write) {
							timeout.tv_sec = 0;
							timeout.tv_usec = std::min((long unsigned)timeout.tv_usec, to_micros_lu(e.second->earliest_next_write - now));
						}
					}
					max = std::max(e.first, max);
				}
			}

			ALWAYS_ASSERT(select(max + 1, &fd_set_read, &fd_set_write, NULL, &timeout) >= 0);

			now = std::chrono::steady_clock::now();
			unsigned char buf[4096];
			{
				std::set<int> remove_set;
				std::lock_guard<std::mutex> lock(me->fd_map_mutex);
				for (const auto& e : me->fd_map) {
					Connection* conn = e.second;

					if (FD_ISSET(e.first, &fd_set_read)) {
						ssize_t count = recv(conn->sock, (char*)buf, 4096, 0);

						std::lock_guard<std::mutex> lock(conn->read_mutex);
						if (count <= 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
							remove_set.insert(e.first);
							conn->sock_errno = errno;
						} else {
							conn->inbound_queue.emplace_back(new std::vector<unsigned char>(buf, buf + count));
							conn->total_inbound_size += count;
							conn->read_cv.notify_all();
						}
					}
					if (FD_ISSET(e.first, &fd_set_write)) {
						if (now < conn->earliest_next_write)
							continue;
						std::lock_guard<std::mutex> lock(conn->send_mutex);
						if (!conn->secondary_writepos && conn->outbound_primary_queue.size()) {
							auto& msg = conn->outbound_primary_queue.front();
							ssize_t count = send(conn->sock, (char*) &(*msg)[conn->primary_writepos], msg->size() - conn->primary_writepos, MSG_NOSIGNAL);
							if (count <= 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
								remove_set.insert(e.first);
								conn->sock_errno = errno;
							} else {
								conn->primary_writepos += count;
								if (conn->primary_writepos == msg->size()) {
									conn->primary_writepos = 0;
									conn->total_waiting_size -= msg->size();
									conn->outbound_primary_queue.pop_front();
								}
							}
						} else {
							assert(conn->outbound_secondary_queue.size() && !conn->primary_writepos);
							auto& msg = conn->outbound_secondary_queue.front();
							ssize_t count = send(conn->sock, (char*) &(*msg)[conn->secondary_writepos], msg->size() - conn->secondary_writepos, MSG_NOSIGNAL);
							if (count <= 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
								remove_set.insert(e.first);
								conn->sock_errno = errno;
							} else {
								conn->secondary_writepos += count;
								if (conn->secondary_writepos == msg->size()) {
									conn->secondary_writepos = 0;
									conn->total_waiting_size -= msg->size();
									conn->outbound_secondary_queue.pop_front();
								}
							}
						}
						if (!conn->total_waiting_size)
							conn->initial_outbound_throttle = false;
						else if (!conn->primary_writepos && !conn->secondary_writepos && conn->initial_outbound_throttle)
							conn->earliest_next_write = std::chrono::steady_clock::now() + std::chrono::milliseconds(20); // Limit outbound to avg 5Mbps worst-case
					}
				}

				for (const int fd : remove_set) {
					Connection* conn = me->fd_map[fd];
					std::lock_guard<std::mutex> lock(conn->read_mutex);
					conn->inbound_queue.emplace_back((std::nullptr_t)NULL);
					conn->read_cv.notify_all();
					conn->disconnectFlags |= DISCONNECT_GLOBAL_THREAD_DONE;
					me->fd_map.erase(fd);
				}
			}
#ifndef WIN32
			if (FD_ISSET(pipefd[0], &fd_set_read))
				while (read(pipefd[0], buf, 4096) > 0);
#endif
		}
	}

	GlobalNetProcess() {
		std::thread(do_net_process, this).detach();
	}
};
static GlobalNetProcess processor;

Connection::~Connection() {
	assert(disconnectFlags & DISCONNECT_COMPLETE);
	user_thread->join();
	close(sock);
	delete user_thread;
}

void Connection::do_send_bytes(const std::shared_ptr<std::vector<unsigned char> >& bytes, int send_mutex_token) {
	if (!send_mutex_token)
		send_mutex.lock();
	else
		ALWAYS_ASSERT(send_mutex_token == outside_send_mutex_token);

	if (initial_outbound_throttle && send_mutex_token)
		initial_outbound_bytes += bytes->size();

	if (total_waiting_size - (initial_outbound_throttle ? initial_outbound_bytes : 0) > 4000000) {
		if (!send_mutex_token)
			send_mutex.unlock();
		return disconnect_from_outside("total_waiting_size blew up :(");
	}

	outbound_primary_queue.push_back(bytes);
	total_waiting_size += bytes->size();
#ifndef WIN32
	if (total_waiting_size > (ssize_t)bytes->size())
		write(processor.pipe_write, "1", 1);
#endif

	if (!send_mutex_token)
		send_mutex.unlock();
}

void Connection::maybe_send_bytes(const std::shared_ptr<std::vector<unsigned char> >& bytes, int send_mutex_token) {
	if (!send_mutex_token) {
		if (!send_mutex.try_lock())
			return;
	} else
		ALWAYS_ASSERT(send_mutex_token == outside_send_mutex_token);

	if (total_waiting_size - (initial_outbound_throttle ? initial_outbound_bytes : 0) > 4000000) {
		if (!send_mutex_token)
			send_mutex.unlock();
		return disconnect_from_outside("total_waiting_size blew up :(");
	}

	outbound_secondary_queue.push_back(bytes);
	total_waiting_size += bytes->size();
#ifndef WIN32
	if (total_waiting_size > (ssize_t)bytes->size())
		write(processor.pipe_write, "1", 1);
#endif

	if (!send_mutex_token)
		send_mutex.unlock();
}

void Connection::disconnect_from_outside(const char* reason) {
	if (disconnectFlags.fetch_or(DISCONNECT_PRINT_AND_CLOSE) & DISCONNECT_PRINT_AND_CLOSE)
		return;

	printf("%s Disconnect: %s (%s)\n", host.c_str(), reason, strerror(errno));
	shutdown(sock, SHUT_RDWR);
}

void Connection::disconnect(const char* reason) {
	if (disconnectFlags.fetch_or(DISCONNECT_STARTED) & DISCONNECT_STARTED)
		return;

	if (!(disconnectFlags.fetch_or(DISCONNECT_PRINT_AND_CLOSE) & DISCONNECT_PRINT_AND_CLOSE)) {
		printf("%s Disconnect: %s (%s)\n", host.c_str(), reason, strerror(sock_errno));
		shutdown(sock, SHUT_RDWR);
	}

	std::unique_lock<std::mutex> lock(read_mutex);
	while (!(disconnectFlags & DISCONNECT_GLOBAL_THREAD_DONE))
		read_cv.wait(lock);

	outbound_secondary_queue.clear();
	outbound_primary_queue.clear();
	inbound_queue.clear();

	disconnectFlags |= DISCONNECT_COMPLETE;

	if (on_disconnect)
		std::thread(on_disconnect).detach();
}

void Connection::do_setup_and_read(Connection* me) {
	#ifdef WIN32
		unsigned long nonblocking = 1;
		ioctlsocket(me->sock, FIONBIO, &nonblocking);
	#else
		fcntl(me->sock, F_SETFL, fcntl(me->sock, F_GETFL) | O_NONBLOCK);
	#endif

	#ifdef X86_BSD
		int nosigpipe = 1;
		setsockopt(me->sock, SOL_SOCKET, SO_NOSIGPIPE, (void *)&nosigpipe, sizeof(int));
	#endif

	int nodelay = 1;
	setsockopt(me->sock, IPPROTO_TCP, TCP_NODELAY, (char*)&nodelay, sizeof(nodelay));

	if (errno) {
		me->disconnectFlags |= DISCONNECT_GLOBAL_THREAD_DONE;
		return me->disconnect("error during connect");
	}

	{
		std::lock_guard<std::mutex> lock(processor.fd_map_mutex);
		processor.fd_map[me->sock] = me;
#ifndef WIN32
		write(processor.pipe_write, "1", 1);
#endif
	}

	me->net_process([&](const char* reason) { me->disconnect(reason); });
}

ssize_t Connection::read_all(char *buf, size_t nbyte) {
	size_t total = 0;
	while (total < nbyte) {
		std::unique_lock<std::mutex> lock(read_mutex);
		while (!inbound_queue.size())
			read_cv.wait(lock);

		if (!inbound_queue.front())
			return -1;

		size_t readamt = std::min(nbyte - total, inbound_queue.front()->size() - readpos);
		memcpy(buf + total, &(*inbound_queue.front())[readpos], readamt);
		if (readpos + readamt == inbound_queue.front()->size()) {
#ifndef WIN32
			int32_t old_size = total_inbound_size;
#endif
			total_inbound_size -= inbound_queue.front()->size();
#ifndef WIN32
			// If the old size is >= 64k, we may need to wakeup the select thread to get it to read more
			if (old_size >= 65536)
				write(processor.pipe_write, "1", 1);
#endif

			readpos = 0;
			inbound_queue.pop_front();
		} else
			readpos += readamt;
		total += readamt;
	}
	assert(total == nbyte);
	return nbyte;
}

void OutboundPersistentConnection::reconnect(std::string disconnectReason) {
	OutboundConnection* old = (OutboundConnection*) connection.fetch_and(0);
	if (old)
		old->disconnect_from_outside(disconnectReason.c_str());

	on_disconnect();

	std::this_thread::sleep_for(std::chrono::seconds(1));
	while (old && !(old->getDisconnectFlags() & DISCONNECT_COMPLETE)) {
		printf("Disconnect of outbound connection still not complete (status is %d)\n", old->getDisconnectFlags());
		std::this_thread::sleep_for(std::chrono::seconds(1));
	}

	std::thread(do_connect, this).detach();
	delete old;
}

void OutboundPersistentConnection::do_connect(OutboundPersistentConnection* me) {
	int sock = socket(AF_INET6, SOCK_STREAM, 0);
	if (sock <= 0)
		return me->reconnect("unable to create socket");

	sockaddr_in6 addr;
	if (!lookup_address(me->serverHost.c_str(), &addr)) {
		close(sock);
		return me->reconnect("unable to lookup host");
	}

	int v6only = 0;
	setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6only, sizeof(v6only));

	addr.sin6_port = htons(me->serverPort);
	if (connect(sock, (struct sockaddr*)&addr, sizeof(addr))) {
		close(sock);
		return me->reconnect("failed to connect()");
	}

	OutboundConnection* new_conn = new OutboundConnection(sock, me);
#ifndef NDEBUG
	unsigned long old_val =
#endif
		me->connection.exchange((unsigned long)new_conn);
	assert(old_val == 0);
	new_conn->construction_done();
}
<commit_msg>Fix pipe creation in non-test<commit_after>#include <assert.h>
#include <string.h>

#ifdef WIN32
	#include <winsock2.h>
	#include <ws2tcpip.h>
	#define SHUT_RDWR SD_BOTH
#else // WIN32
	#include <sys/socket.h>
	#include <netinet/in.h>
	#include <netinet/tcp.h>
	#include <netdb.h>
	#include <fcntl.h>
#endif // !WIN32

#include <unordered_map>
#include <set>

#include "connection.h"

#include "utils.h"

class GlobalNetProcess {
public:
	std::mutex fd_map_mutex;
	std::unordered_map<int, Connection*> fd_map;
#ifndef WIN32
	int pipe_write;
#endif

	static void do_net_process(GlobalNetProcess* me) {
		fd_set fd_set_read, fd_set_write;
		struct timeval timeout;

#ifndef WIN32
		int pipefd[2];
		ALWAYS_ASSERT(!pipe(pipefd));
		fcntl(pipefd[1], F_SETFL, fcntl(pipefd[1], F_GETFL) | O_NONBLOCK);
		fcntl(pipefd[0], F_SETFL, fcntl(pipefd[0], F_GETFL) | O_NONBLOCK);
		me->pipe_write = pipefd[1];
#endif

		while (true) {
#ifndef WIN32
			timeout.tv_sec = 86400;
			timeout.tv_usec = 20 * 1000;
#else
			timeout.tv_sec = 0;
			timeout.tv_usec = 1000;
#endif

			FD_ZERO(&fd_set_read); FD_ZERO(&fd_set_write);
#ifndef WIN32
			int max = pipefd[0];
			FD_SET(pipefd[0], &fd_set_read);
#else
			int max = 0;
#endif
			auto now = std::chrono::steady_clock::now();
			{
				std::lock_guard<std::mutex> lock(me->fd_map_mutex);
				for (const auto& e : me->fd_map) {
					ALWAYS_ASSERT(e.first < FD_SETSIZE);
					if (e.second->total_inbound_size < 65536)
						FD_SET(e.first, &fd_set_read);
					if (e.second->total_waiting_size > 0) {
						FD_SET(e.first, &fd_set_write);
						if (now < e.second->earliest_next_write) {
							timeout.tv_sec = 0;
							timeout.tv_usec = std::min((long unsigned)timeout.tv_usec, to_micros_lu(e.second->earliest_next_write - now));
						}
					}
					max = std::max(e.first, max);
				}
			}

			ALWAYS_ASSERT(select(max + 1, &fd_set_read, &fd_set_write, NULL, &timeout) >= 0);

			now = std::chrono::steady_clock::now();
			unsigned char buf[4096];
			{
				std::set<int> remove_set;
				std::lock_guard<std::mutex> lock(me->fd_map_mutex);
				for (const auto& e : me->fd_map) {
					Connection* conn = e.second;

					if (FD_ISSET(e.first, &fd_set_read)) {
						ssize_t count = recv(conn->sock, (char*)buf, 4096, 0);

						std::lock_guard<std::mutex> lock(conn->read_mutex);
						if (count <= 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
							remove_set.insert(e.first);
							conn->sock_errno = errno;
						} else {
							conn->inbound_queue.emplace_back(new std::vector<unsigned char>(buf, buf + count));
							conn->total_inbound_size += count;
							conn->read_cv.notify_all();
						}
					}
					if (FD_ISSET(e.first, &fd_set_write)) {
						if (now < conn->earliest_next_write)
							continue;
						std::lock_guard<std::mutex> lock(conn->send_mutex);
						if (!conn->secondary_writepos && conn->outbound_primary_queue.size()) {
							auto& msg = conn->outbound_primary_queue.front();
							ssize_t count = send(conn->sock, (char*) &(*msg)[conn->primary_writepos], msg->size() - conn->primary_writepos, MSG_NOSIGNAL);
							if (count <= 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
								remove_set.insert(e.first);
								conn->sock_errno = errno;
							} else {
								conn->primary_writepos += count;
								if (conn->primary_writepos == msg->size()) {
									conn->primary_writepos = 0;
									conn->total_waiting_size -= msg->size();
									conn->outbound_primary_queue.pop_front();
								}
							}
						} else {
							assert(conn->outbound_secondary_queue.size() && !conn->primary_writepos);
							auto& msg = conn->outbound_secondary_queue.front();
							ssize_t count = send(conn->sock, (char*) &(*msg)[conn->secondary_writepos], msg->size() - conn->secondary_writepos, MSG_NOSIGNAL);
							if (count <= 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
								remove_set.insert(e.first);
								conn->sock_errno = errno;
							} else {
								conn->secondary_writepos += count;
								if (conn->secondary_writepos == msg->size()) {
									conn->secondary_writepos = 0;
									conn->total_waiting_size -= msg->size();
									conn->outbound_secondary_queue.pop_front();
								}
							}
						}
						if (!conn->total_waiting_size)
							conn->initial_outbound_throttle = false;
						else if (!conn->primary_writepos && !conn->secondary_writepos && conn->initial_outbound_throttle)
							conn->earliest_next_write = std::chrono::steady_clock::now() + std::chrono::milliseconds(20); // Limit outbound to avg 5Mbps worst-case
					}
				}

				for (const int fd : remove_set) {
					Connection* conn = me->fd_map[fd];
					std::lock_guard<std::mutex> lock(conn->read_mutex);
					conn->inbound_queue.emplace_back((std::nullptr_t)NULL);
					conn->read_cv.notify_all();
					conn->disconnectFlags |= DISCONNECT_GLOBAL_THREAD_DONE;
					me->fd_map.erase(fd);
				}
			}
#ifndef WIN32
			if (FD_ISSET(pipefd[0], &fd_set_read))
				while (read(pipefd[0], buf, 4096) > 0);
#endif
		}
	}

	GlobalNetProcess() {
		std::thread(do_net_process, this).detach();
	}
};
static GlobalNetProcess processor;

Connection::~Connection() {
	assert(disconnectFlags & DISCONNECT_COMPLETE);
	user_thread->join();
	close(sock);
	delete user_thread;
}

void Connection::do_send_bytes(const std::shared_ptr<std::vector<unsigned char> >& bytes, int send_mutex_token) {
	if (!send_mutex_token)
		send_mutex.lock();
	else
		ALWAYS_ASSERT(send_mutex_token == outside_send_mutex_token);

	if (initial_outbound_throttle && send_mutex_token)
		initial_outbound_bytes += bytes->size();

	if (total_waiting_size - (initial_outbound_throttle ? initial_outbound_bytes : 0) > 4000000) {
		if (!send_mutex_token)
			send_mutex.unlock();
		return disconnect_from_outside("total_waiting_size blew up :(");
	}

	outbound_primary_queue.push_back(bytes);
	total_waiting_size += bytes->size();
#ifndef WIN32
	if (total_waiting_size > (ssize_t)bytes->size())
		write(processor.pipe_write, "1", 1);
#endif

	if (!send_mutex_token)
		send_mutex.unlock();
}

void Connection::maybe_send_bytes(const std::shared_ptr<std::vector<unsigned char> >& bytes, int send_mutex_token) {
	if (!send_mutex_token) {
		if (!send_mutex.try_lock())
			return;
	} else
		ALWAYS_ASSERT(send_mutex_token == outside_send_mutex_token);

	if (total_waiting_size - (initial_outbound_throttle ? initial_outbound_bytes : 0) > 4000000) {
		if (!send_mutex_token)
			send_mutex.unlock();
		return disconnect_from_outside("total_waiting_size blew up :(");
	}

	outbound_secondary_queue.push_back(bytes);
	total_waiting_size += bytes->size();
#ifndef WIN32
	if (total_waiting_size > (ssize_t)bytes->size())
		write(processor.pipe_write, "1", 1);
#endif

	if (!send_mutex_token)
		send_mutex.unlock();
}

void Connection::disconnect_from_outside(const char* reason) {
	if (disconnectFlags.fetch_or(DISCONNECT_PRINT_AND_CLOSE) & DISCONNECT_PRINT_AND_CLOSE)
		return;

	printf("%s Disconnect: %s (%s)\n", host.c_str(), reason, strerror(errno));
	shutdown(sock, SHUT_RDWR);
}

void Connection::disconnect(const char* reason) {
	if (disconnectFlags.fetch_or(DISCONNECT_STARTED) & DISCONNECT_STARTED)
		return;

	if (!(disconnectFlags.fetch_or(DISCONNECT_PRINT_AND_CLOSE) & DISCONNECT_PRINT_AND_CLOSE)) {
		printf("%s Disconnect: %s (%s)\n", host.c_str(), reason, strerror(sock_errno));
		shutdown(sock, SHUT_RDWR);
	}

	std::unique_lock<std::mutex> lock(read_mutex);
	while (!(disconnectFlags & DISCONNECT_GLOBAL_THREAD_DONE))
		read_cv.wait(lock);

	outbound_secondary_queue.clear();
	outbound_primary_queue.clear();
	inbound_queue.clear();

	disconnectFlags |= DISCONNECT_COMPLETE;

	if (on_disconnect)
		std::thread(on_disconnect).detach();
}

void Connection::do_setup_and_read(Connection* me) {
	#ifdef WIN32
		unsigned long nonblocking = 1;
		ioctlsocket(me->sock, FIONBIO, &nonblocking);
	#else
		fcntl(me->sock, F_SETFL, fcntl(me->sock, F_GETFL) | O_NONBLOCK);
	#endif

	#ifdef X86_BSD
		int nosigpipe = 1;
		setsockopt(me->sock, SOL_SOCKET, SO_NOSIGPIPE, (void *)&nosigpipe, sizeof(int));
	#endif

	int nodelay = 1;
	setsockopt(me->sock, IPPROTO_TCP, TCP_NODELAY, (char*)&nodelay, sizeof(nodelay));

	if (errno) {
		me->disconnectFlags |= DISCONNECT_GLOBAL_THREAD_DONE;
		return me->disconnect("error during connect");
	}

	{
		std::lock_guard<std::mutex> lock(processor.fd_map_mutex);
		processor.fd_map[me->sock] = me;
#ifndef WIN32
		write(processor.pipe_write, "1", 1);
#endif
	}

	me->net_process([&](const char* reason) { me->disconnect(reason); });
}

ssize_t Connection::read_all(char *buf, size_t nbyte) {
	size_t total = 0;
	while (total < nbyte) {
		std::unique_lock<std::mutex> lock(read_mutex);
		while (!inbound_queue.size())
			read_cv.wait(lock);

		if (!inbound_queue.front())
			return -1;

		size_t readamt = std::min(nbyte - total, inbound_queue.front()->size() - readpos);
		memcpy(buf + total, &(*inbound_queue.front())[readpos], readamt);
		if (readpos + readamt == inbound_queue.front()->size()) {
#ifndef WIN32
			int32_t old_size = total_inbound_size;
#endif
			total_inbound_size -= inbound_queue.front()->size();
#ifndef WIN32
			// If the old size is >= 64k, we may need to wakeup the select thread to get it to read more
			if (old_size >= 65536)
				write(processor.pipe_write, "1", 1);
#endif

			readpos = 0;
			inbound_queue.pop_front();
		} else
			readpos += readamt;
		total += readamt;
	}
	assert(total == nbyte);
	return nbyte;
}

void OutboundPersistentConnection::reconnect(std::string disconnectReason) {
	OutboundConnection* old = (OutboundConnection*) connection.fetch_and(0);
	if (old)
		old->disconnect_from_outside(disconnectReason.c_str());

	on_disconnect();

	std::this_thread::sleep_for(std::chrono::seconds(1));
	while (old && !(old->getDisconnectFlags() & DISCONNECT_COMPLETE)) {
		printf("Disconnect of outbound connection still not complete (status is %d)\n", old->getDisconnectFlags());
		std::this_thread::sleep_for(std::chrono::seconds(1));
	}

	std::thread(do_connect, this).detach();
	delete old;
}

void OutboundPersistentConnection::do_connect(OutboundPersistentConnection* me) {
	int sock = socket(AF_INET6, SOCK_STREAM, 0);
	if (sock <= 0)
		return me->reconnect("unable to create socket");

	sockaddr_in6 addr;
	if (!lookup_address(me->serverHost.c_str(), &addr)) {
		close(sock);
		return me->reconnect("unable to lookup host");
	}

	int v6only = 0;
	setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6only, sizeof(v6only));

	addr.sin6_port = htons(me->serverPort);
	if (connect(sock, (struct sockaddr*)&addr, sizeof(addr))) {
		close(sock);
		return me->reconnect("failed to connect()");
	}

	OutboundConnection* new_conn = new OutboundConnection(sock, me);
#ifndef NDEBUG
	unsigned long old_val =
#endif
		me->connection.exchange((unsigned long)new_conn);
	assert(old_val == 0);
	new_conn->construction_done();
}
<|endoftext|>


<commit_before>/*
This file is part of WME Lite.
http://dead-code.org/redir.php?target=wmelite

Copyright (c) 2011 Jan Nedoma

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include "dcgf.h"
#include "SXArray.h"

IMPLEMENT_PERSISTENT(CSXArray, false);

//////////////////////////////////////////////////////////////////////////
CSXArray::CSXArray(CBGame* inGame, CScStack* Stack):CBScriptable(inGame)
{
	m_Length = 0;
	m_Values = new CScValue(Game);

	int NumParams = Stack->Pop()->GetInt(0);

	// Originally there was 1 and >1
	// This made a serious problem when called a = new Array("X"); as the
	// item got swallowed and game would break.
	// NEED to ask if there are any issues I've just caused with this fix...

	if(NumParams==0) m_Length = Stack->Pop()->GetInt(0);
	else if(NumParams>=1){
		m_Length = NumParams;
		char ParamName[20];
		for(int i=0; i<NumParams; i++){
			sprintf(ParamName, "%d", i);
			m_Values->SetProp(ParamName, Stack->Pop());
		}
	}
}

//////////////////////////////////////////////////////////////////////////
CSXArray::CSXArray(CBGame* inGame):CBScriptable(inGame)
{
	m_Length = 0;
	m_Values = new CScValue(Game);
}

//////////////////////////////////////////////////////////////////////////
CSXArray::~CSXArray()
{
	SAFE_DELETE(m_Values);
}

//////////////////////////////////////////////////////////////////////////
char* CSXArray::ScToString()
{
	static char Dummy[32768];
	strcpy(Dummy, "");
	char PropName[20];
	for(int i=0; i<m_Length; i++){
		sprintf(PropName, "%d", i);
		CScValue* val = m_Values->GetProp(PropName);
		if(val){
			if(strlen(Dummy) + strlen(val->GetString()) < 32768){
				strcat(Dummy, val->GetString());
			}
		}

		if(i<m_Length-1 && strlen(Dummy)+1 < 32768) strcat(Dummy, ",");
	}
	return Dummy;
}

//////////////////////////////////////////////////////////////////////////
HRESULT CSXArray::ScCallMethod(CScScript* Script, CScStack *Stack, CScStack *ThisStack, char *Name)
{
	//////////////////////////////////////////////////////////////////////////
	// Push
	//////////////////////////////////////////////////////////////////////////
	if(strcmp(Name, "Push")==0){
		int NumParams = Stack->Pop()->GetInt(0);
		char ParamName[20];

		for(int i=0; i<NumParams; i++){
			m_Length++;
			sprintf(ParamName, "%d", m_Length-1);
			m_Values->SetProp(ParamName, Stack->Pop(), true);
		}
		Stack->PushInt(m_Length);

		return S_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// Pop
	//////////////////////////////////////////////////////////////////////////
	if(strcmp(Name, "Pop")==0){

		Stack->CorrectParams(0);

		if(m_Length>0){
			char ParamName[20];
			sprintf(ParamName, "%d", m_Length-1);
			Stack->Push(m_Values->GetProp(ParamName));
			m_Values->DeleteProp(ParamName);
			m_Length--;
		}
		else Stack->PushNULL();

		return S_OK;
	}

	else return E_FAIL;
}

//////////////////////////////////////////////////////////////////////////
CScValue* CSXArray::ScGetProperty(char *Name)
{
	m_ScValue->SetNULL();

	//////////////////////////////////////////////////////////////////////////
	// Type
	//////////////////////////////////////////////////////////////////////////
	if(strcmp(Name, "Type")==0){
		m_ScValue->SetString("array");
		return m_ScValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// Length
	//////////////////////////////////////////////////////////////////////////
	else if(strcmp(Name, "Length")==0){
		m_ScValue->SetInt(m_Length);
		return m_ScValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// [number]
	//////////////////////////////////////////////////////////////////////////
	else{
		char ParamName[20];
		if(ValidNumber(Name, ParamName)){
			return m_Values->GetProp(ParamName);
		}
		else return m_ScValue;
	}
}

//////////////////////////////////////////////////////////////////////////
HRESULT CSXArray::ScSetProperty(char *Name, CScValue *Value)
{
	//////////////////////////////////////////////////////////////////////////
	// Length
	//////////////////////////////////////////////////////////////////////////
	if(strcmp(Name, "Length")==0){
		int OrigLength = m_Length;
		m_Length = max(Value->GetInt(0), 0);

		char PropName[20];
		if(m_Length < OrigLength){
			for(int i=m_Length; i<OrigLength; i++){
				sprintf(PropName, "%d", i);
				m_Values->DeleteProp(PropName);
			}
		}
		return S_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// [number]
	//////////////////////////////////////////////////////////////////////////
	else{
		char ParamName[20];
		if(ValidNumber(Name, ParamName)){
			int Index = atoi(ParamName);
			if(Index>=m_Length) m_Length = Index + 1;
			return m_Values->SetProp(ParamName, Value);
		}
		else return E_FAIL;
	}
}

//////////////////////////////////////////////////////////////////////////
HRESULT CSXArray::Persist(CBPersistMgr* PersistMgr)
{
	CBScriptable::Persist(PersistMgr);

	PersistMgr->Transfer(TMEMBER(m_Length));
	PersistMgr->Transfer(TMEMBER(m_Values));

	return S_OK;
}

//////////////////////////////////////////////////////////////////////////
bool CSXArray::ValidNumber(const char *OrigStr, char *OutStr)
{
	bool IsNumber = true;
	for(int i=0; i<strlen(OrigStr); i++){
		if(!isdigit(OrigStr[i])){
			IsNumber = false;
			break;
		}
	}

	if(IsNumber){
		int Index = atoi(OrigStr);
		sprintf(OutStr, "%d", Index);
		return true;
	}
	else return false;
}

//////////////////////////////////////////////////////////////////////////
HRESULT CSXArray::Push(CScValue* Val)
{
	char ParamName[20];
	m_Length++;
	sprintf(ParamName, "%d", m_Length-1);
	m_Values->SetProp(ParamName, Val, true);
	return S_OK;
}<commit_msg>Fixed Stack underflow...<commit_after>/*
This file is part of WME Lite.
http://dead-code.org/redir.php?target=wmelite

Copyright (c) 2011 Jan Nedoma

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include "dcgf.h"
#include "SXArray.h"

IMPLEMENT_PERSISTENT(CSXArray, false);

//////////////////////////////////////////////////////////////////////////
CSXArray::CSXArray(CBGame* inGame, CScStack* Stack):CBScriptable(inGame)
{
	m_Length = 0;
	m_Values = new CScValue(Game);

	int NumParams = Stack->Pop()->GetInt(0);

	// Originally there was 1 and >1
	// This made a serious problem when called a = new Array("X"); as the
	// item got swallowed and game would break.
	// NEED to ask if there are any issues I've just caused with this fix...

//	if(NumParams==0) m_Length = Stack->Pop()->GetInt(0);

	if(NumParams>=1){
		m_Length = NumParams;
		char ParamName[20];
		for(int i=0; i<NumParams; i++){
			sprintf(ParamName, "%d", i);
			m_Values->SetProp(ParamName, Stack->Pop());
		}
	}
}

//////////////////////////////////////////////////////////////////////////
CSXArray::CSXArray(CBGame* inGame):CBScriptable(inGame)
{
	m_Length = 0;
	m_Values = new CScValue(Game);
}

//////////////////////////////////////////////////////////////////////////
CSXArray::~CSXArray()
{
	SAFE_DELETE(m_Values);
}

//////////////////////////////////////////////////////////////////////////
char* CSXArray::ScToString()
{
	static char Dummy[32768];
	strcpy(Dummy, "");
	char PropName[20];
	for(int i=0; i<m_Length; i++){
		sprintf(PropName, "%d", i);
		CScValue* val = m_Values->GetProp(PropName);
		if(val){
			if(strlen(Dummy) + strlen(val->GetString()) < 32768){
				strcat(Dummy, val->GetString());
			}
		}

		if(i<m_Length-1 && strlen(Dummy)+1 < 32768) strcat(Dummy, ",");
	}
	return Dummy;
}

//////////////////////////////////////////////////////////////////////////
HRESULT CSXArray::ScCallMethod(CScScript* Script, CScStack *Stack, CScStack *ThisStack, char *Name)
{
	//////////////////////////////////////////////////////////////////////////
	// Push
	//////////////////////////////////////////////////////////////////////////
	if(strcmp(Name, "Push")==0){
		int NumParams = Stack->Pop()->GetInt(0);
		char ParamName[20];

		for(int i=0; i<NumParams; i++){
			m_Length++;
			sprintf(ParamName, "%d", m_Length-1);
			m_Values->SetProp(ParamName, Stack->Pop(), true);
		}
		Stack->PushInt(m_Length);

		return S_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// Pop
	//////////////////////////////////////////////////////////////////////////
	if(strcmp(Name, "Pop")==0){

		Stack->CorrectParams(0);

		if(m_Length>0){
			char ParamName[20];
			sprintf(ParamName, "%d", m_Length-1);
			Stack->Push(m_Values->GetProp(ParamName));
			m_Values->DeleteProp(ParamName);
			m_Length--;
		}
		else Stack->PushNULL();

		return S_OK;
	}

	else return E_FAIL;
}

//////////////////////////////////////////////////////////////////////////
CScValue* CSXArray::ScGetProperty(char *Name)
{
	m_ScValue->SetNULL();

	//////////////////////////////////////////////////////////////////////////
	// Type
	//////////////////////////////////////////////////////////////////////////
	if(strcmp(Name, "Type")==0){
		m_ScValue->SetString("array");
		return m_ScValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// Length
	//////////////////////////////////////////////////////////////////////////
	else if(strcmp(Name, "Length")==0){
		m_ScValue->SetInt(m_Length);
		return m_ScValue;
	}

	//////////////////////////////////////////////////////////////////////////
	// [number]
	//////////////////////////////////////////////////////////////////////////
	else{
		char ParamName[20];
		if(ValidNumber(Name, ParamName)){
			return m_Values->GetProp(ParamName);
		}
		else return m_ScValue;
	}
}

//////////////////////////////////////////////////////////////////////////
HRESULT CSXArray::ScSetProperty(char *Name, CScValue *Value)
{
	//////////////////////////////////////////////////////////////////////////
	// Length
	//////////////////////////////////////////////////////////////////////////
	if(strcmp(Name, "Length")==0){
		int OrigLength = m_Length;
		m_Length = max(Value->GetInt(0), 0);

		char PropName[20];
		if(m_Length < OrigLength){
			for(int i=m_Length; i<OrigLength; i++){
				sprintf(PropName, "%d", i);
				m_Values->DeleteProp(PropName);
			}
		}
		return S_OK;
	}

	//////////////////////////////////////////////////////////////////////////
	// [number]
	//////////////////////////////////////////////////////////////////////////
	else{
		char ParamName[20];
		if(ValidNumber(Name, ParamName)){
			int Index = atoi(ParamName);
			if(Index>=m_Length) m_Length = Index + 1;
			return m_Values->SetProp(ParamName, Value);
		}
		else return E_FAIL;
	}
}

//////////////////////////////////////////////////////////////////////////
HRESULT CSXArray::Persist(CBPersistMgr* PersistMgr)
{
	CBScriptable::Persist(PersistMgr);

	PersistMgr->Transfer(TMEMBER(m_Length));
	PersistMgr->Transfer(TMEMBER(m_Values));

	return S_OK;
}

//////////////////////////////////////////////////////////////////////////
bool CSXArray::ValidNumber(const char *OrigStr, char *OutStr)
{
	bool IsNumber = true;
	for(int i=0; i<strlen(OrigStr); i++){
		if(!isdigit(OrigStr[i])){
			IsNumber = false;
			break;
		}
	}

	if(IsNumber){
		int Index = atoi(OrigStr);
		sprintf(OutStr, "%d", Index);
		return true;
	}
	else return false;
}

//////////////////////////////////////////////////////////////////////////
HRESULT CSXArray::Push(CScValue* Val)
{
	char ParamName[20];
	m_Length++;
	sprintf(ParamName, "%d", m_Length-1);
	m_Values->SetProp(ParamName, Val, true);
	return S_OK;
}<|endoftext|>


<commit_before><commit_msg>build: boost: Clean up branches requiring boost-1.55<commit_after><|endoftext|>


<commit_before>#include <iostream>
#include <ctime>
#include <cstdlib>
#include <fstream>
#include <sstream>
#include <vector>
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"

using namespace cv;
Mat CalculerContour(Mat& mat1);
int main ( int argc,char **argv ) {
	/*Mat mat1 = imread("image.jpg", 1);
	char d[12] = {255,0,0,0,255,0,0,0,255,0,0,0};
	Mat mat2(2,2,CV_8UC3,d);
	Mat res = CalculerContour(mat1);
	imwrite("img.jpg",res);*/

	Mat img = imread("pic_init.jpg",0);
	Mat res;
	int i = 0;
	if(!img.empty())
		threshold(img,res,0,255,THRESH_BINARY_INV+THRESH_OTSU); // Seuil via algo OTSU : convertit une image en une image binaire ("vrai" noir et blanc)
	else
		std::cout << "img is empty" << '\n';
	imwrite("threshold_im.jpg",res);
	std::vector<std::vector<Point> > contours;
	std::vector<Vec4i> hierarchy;
	if(!res.empty())
//		findContours(res,contours,hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE); // Recherche des contours des composantes connexes de l'image avec hiérarchie (donc plus long)
		findContours(res,contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE); // Recherche des contours des composantes connexes de l'image
	else
		std::cout << "res is empty" << '\n';
/*	for(i=0;i>=0;i = hierarchy[i][0]) //Remplissage des contours avec hiérarchie (donc plus long)
	{
		Scalar color (rand()%255, rand()%255, rand()%255);
		drawContours(res,contours,i,color,CV_FILLED, 8, hierarchy);

	}*/
	for(i=0; i<contours.size(); i++) // Remplissage des contours
	{
//		Rect r = boundingRect(contours[i]); // Rectangle englobant une composante connexe (fonctionnement bizarre)
		Scalar color (rand()%255, rand()%255, rand()%255);
		drawContours(res,contours,i,color,CV_FILLED, 8);

	}
	imwrite("connected_comp.jpg", res);
}

Mat CalculerContour(Mat& mat1)
{
	unsigned char * input = (unsigned char *)(mat1.data);
	int x_min = 9999;
	int x_max = -1;
	int y_min = 9999;
	int y_max = -1;
	int b,g,r;
	for (int i = 0 ; i < mat1.rows ; i++) { //960
   		for (int j = 0 ; j < 3*mat1.cols ; j = j + 3) { //1280
			b = (int)input[3*mat1.cols*i+j];
			g = (int)input[3*mat1.cols*i+j+1];
			r = (int)input[3*mat1.cols*i+j+2];
			if ((b < 90) && (r < 90) && (g < 90))
			{
			    x_min = ((x_min > j/3)? j/3 : x_min) ;
			    x_max = ((x_max < j/3)? j/3 : x_max) ;
			    y_min = ((y_min > i)? i : y_min) ;
			    y_max = ((y_max < i)? i : y_max) ;
			}
	   	}
	}
//	std::cout << mat1 << '\n';
	std::cout << "x_min :" << x_min << " x_max :" << x_max << " y_min : " << y_min << " y_max : " << y_max << '\n';
	for (int i = y_min ; i < y_max ; i++) { // 960
		for (int j = x_min ; j < 3*x_max ; j = j + 3) { // 1280
			input[3*mat1.cols*i+j] = 0;
			input[3*mat1.cols*i+j+1] = 0;
			input[3*mat1.cols*i+j+2] = 255;
   		}
	}
	Mat ret(Size(1280,960),CV_8UC3, input);
	return (ret);
}
<commit_msg>C5 : Upgraded : color filling for labelisation. Upgraded : contour finding method. Added : Now consider equal sign as one symbol instead of two.<commit_after>#include <iostream>
#include <ctime>
#include <cstdlib>
#include <fstream>
#include <sstream>
#include <vector>
#include <algorithm>
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/core/core.hpp"

using namespace cv;
//Mat CalculerContour(Mat& mat1);
int main ( int argc,char **argv ) {
	/*Mat mat1 = imread("image.jpg", 1);
	char d[12] = {255,0,0,0,255,0,0,0,255,0,0,0};
	Mat mat2(2,2,CV_8UC3,d);
	Mat res = CalculerContour(mat1);
	imwrite("img.jpg",res);*/

	Mat img = imread("pic_init.jpg",0);
	Mat bin;
	Mat res;
	int i = 0;
	int j = 0;
	int k = 0;
	double col;
	int size_cont;
	int size_1;
	int size_2;
	int size_tot;
	int medv2;
	int debv1;
	int endv1;
	int b = 0;
	RNG rng(12345);
	if(!img.empty())
		threshold(img,bin,0,255,THRESH_BINARY_INV+THRESH_OTSU); // Seuil via algo OTSU : convertit une image en une image binaire ("vrai" noir et blanc)
	else
		std::cout << "img is empty" << '\n';
	imwrite("threshold_im.jpg",bin);
	std::vector<std::vector<Point> > contours;
	std::vector<Vec4i> hierarchy;
	std::vector<Point> cont_eq;
	std::vector<Point> cont_test;
	std::vector<Point> cont_eq_1;
	std::vector<Point> cont_eq_2;
	std::vector<int> vect_x_1;
	std::vector<int> vect_x_2;
	if(!bin.empty())
//		findContours(res,contours,hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE); // Recherche des contours des composantes connexes de l'image avec hiérarchie (donc plus long)
		findContours(bin,contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_TC89_L1); // Recherche des contours des composantes connexes de l'image
	else
		std::cout << "bin is empty" << '\n';
/*	for(i=0;i>=0;i = hierarchy[i][0]) //Remplissage des contours avec hiérarchie (donc plus long)
	{
		Scalar color (rand()%255, rand()%255, rand()%255);
		drawContours(res,contours,i,color,CV_FILLED, 8, hierarchy);

	}*/
	res = Mat::zeros(bin.size(), CV_8UC3);
	std::vector<std::vector<Point> > contours_filt;
	for(i=0; i<contours.size(); i++)
	{
		if (contours[i].size() > 100)
		{
			contours_filt.push_back(contours[i]);
		}
	}
	size_cont = contours_filt.size();
	for(i=0; i<size_cont; i++)
	{
	if(b == 0)
	{
		for(j=0; j<contours_filt[i].size(); j++)
		{
			vect_x_1.push_back(contours_filt[i][j].x);
			std::sort(vect_x_1.begin(),vect_x_1.end());
		}
		for(k=i+1;k<size_cont;k++)
		{
		if(b==0)
		{
			for(j=0; j<contours_filt[k].size(); j++)
			{
				vect_x_2.push_back(contours_filt[k][j].x);
				std::sort(vect_x_2.begin(),vect_x_2.end());
			}
			medv2 = vect_x_2[vect_x_2.size()/2];
			debv1 = vect_x_1[0];
			endv1 = vect_x_1[vect_x_1.size()-1];

				std::cout << "i : " << i << '\n';
				std::cout << "k : " << k << '\n';
				std::cout << "medv2 : " << medv2 << '\n';
				std::cout << "debv1 : " << debv1 << '\n';
				std::cout << "endv1 : " << endv1 << '\n';
			if(medv2 > debv1 && medv2 < endv1)
			{
				contours_filt[k].swap(contours_filt[size_cont-2]);
				contours_filt[i].swap(contours_filt[size_cont-1]);
				b = 1;
			}
		}
		}
	}
	}
	cont_eq_1 = contours_filt[size_cont-2];
	cont_eq_2 = contours_filt[size_cont-1];
	size_1 = cont_eq_1.size();
	size_2 = cont_eq_2.size();
	size_tot = size_1+size_2;
	for(i=0;i<size_1;i++)
	{
		cont_eq.push_back(cont_eq_1.back());
		cont_eq_1.pop_back();
	}
	for(i=0;i<size_2;i++)
	{
		cont_eq.push_back(cont_eq_2.back());
		cont_eq_2.pop_back();
	}
	contours_filt.pop_back();
	contours_filt.pop_back();
	contours_filt.push_back(cont_eq);
	std::cout << contours_filt.size() << '\n';
	for(i=0; i<contours_filt.size(); i++) // Remplissage des contours
	{
		//Rect r = boundingRect(contours[i]); // Rectangle englobant une composante connexe (fonctionnement bizarre)
		Scalar color = Scalar(rng.uniform(0,255),rng.uniform(0,255),rng.uniform(0,255));
		//std::cout << contours[i] << '\n';
		//std::cout << i << '\n';
		//col = rand()&255;
		std::cout << "couleur : " << color[0]<< " " << color[1]<< " " << color[2]<< '\n';
		drawContours(res,contours_filt,i,color,CV_FILLED, 8);
	}

//	cvtColor(img,res,CV_BGR2GRAY);
	/*Canny(img,res,100,200,3);
	imwrite("threshold_im.jpg",res);
	RNG rng(12345);
	findContours(res,contours,hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0,0)); // Recherche des contours des composantes connexes de l'image
	Mat drawing = Mat::zeros(res.size(), CV_8UC3);
	for(i=0; i<contours.size(); i++) // Remplissage des contours
	{
		Scalar color = Scalar(rand()&255, rand()&255, rand()&255);
		drawContours(drawing,contours,i,color,2,8,hierarchy,0,Point() );
	}
*/

	imwrite("connected_comp.jpg", res);
}
/*
Mat CalculerContour(Mat& mat1)
{
	unsigned char * input = (unsigned char *)(mat1.data);
	int x_min = 9999;
	int x_max = -1;
	int y_min = 9999;
	int y_max = -1;
	int b,g,r;
	for (int i = 0 ; i < mat1.rows ; i++) { //960
   		for (int j = 0 ; j < 3*mat1.cols ; j = j + 3) { //1280
			b = (int)input[3*mat1.cols*i+j];
			g = (int)input[3*mat1.cols*i+j+1];
			r = (int)input[3*mat1.cols*i+j+2];
			if ((b < 90) && (r < 90) && (g < 90))
			{
			    x_min = ((x_min > j/3)? j/3 : x_min) ;
			    x_max = ((x_max < j/3)? j/3 : x_max) ;
			    y_min = ((y_min > i)? i : y_min) ;
			    y_max = ((y_max < i)? i : y_max) ;
			}
	   	}
	}
//	std::cout << mat1 << '\n';
	std::cout << "x_min :" << x_min << " x_max :" << x_max << " y_min : " << y_min << " y_max : " << y_max << '\n';
	for (int i = y_min ; i < y_max ; i++) { // 960
		for (int j = x_min ; j < 3*x_max ; j = j + 3) { // 1280
			input[3*mat1.cols*i+j] = 0;
			input[3*mat1.cols*i+j+1] = 0;
			input[3*mat1.cols*i+j+2] = 255;
   		}
	}
	Mat ret(Size(1280,960),CV_8UC3, input);
	return (ret);
}
*/
<|endoftext|>


<commit_before>/* The MIT License:

Copyright (c) 2009-2011 Ivan Gagis <igagis@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */

//Homepage: http://code.google.com/p/cliser/

/**
 * @file Connection.hpp
 * @author Ivan Gagis <igagis@gmail.com>
 * @brief Connection base class.
 * Connection base class. User is supposed to derive his own connection
 * object from it.
 */

#pragma once

#include <ting/types.hpp>
#include <ting/Array.hpp>
#include <ting/Buffer.hpp>
#include <ting/Ref.hpp>
#include <ting/Socket.hpp>
#include <ting/Thread.hpp>

namespace cliser{

//forward declarations
class ConnectionsThread;

class Connection : public virtual ting::RefCounted{
	friend class ConnectionsThread;
	friend class ServerThread;
	friend class ClientThread;

	//This is the network data associated with Connection
	std::list<ting::Array<ting::u8> > packetQueue;
	unsigned dataSent;//number of bytes sent from first packet in the queue
	//~

	ting::TCPSocket socket;

	//NOTE: clientThread may be accessed from different threads, therefore, protect it with mutex
	ConnectionsThread *parentThread;
	ting::Mutex parentThreadMutex;

	ting::Array<ting::u8> receivedData;//Should be protected with mutex

	inline void SetHandlingThread(ConnectionsThread *thr){
		ASSERT(thr)
		ting::Mutex::Guard mutexGuard(this->parentThreadMutex);
		//Assert that client is not added to some thread already.
		ASSERT_INFO(!this->parentThread, "client's handler thread is already set")
		this->parentThread = thr;
	}

	inline void ClearHandlingThread(){
		ting::Mutex::Guard mutexGuard(this->parentThreadMutex);
		this->parentThread = 0;
	}

protected:
	ting::Mutex receivedDataMutex;

	inline Connection() :
			parentThread(0)
	{}

public:
	virtual ~Connection(){
//		TRACE(<< "Connection::" << __func__ << "(): invoked" << std::endl)
	}

	void Disconnect_ts();

	void Send_ts(ting::Array<ting::u8> data);
	void SendCopy_ts(const ting::Buffer<ting::u8>& data);

	ting::Array<ting::u8> GetReceivedData_ts();
};

class Listener{

	//NOTE: this numTimesAdded variable is only needed to check that
	//      the listener object has not been deleted before its owner
	//      (the object which does notifications through this listener).
	friend class cliser::ConnectionsThread;
	friend class ServerThread;
	ting::Inited<unsigned, 0> numTimesAdded;

public:

	virtual ting::Ref<cliser::Connection> CreateConnectionObject() = 0;

	virtual void OnConnected_ts(const ting::Ref<Connection>& c) = 0;

	virtual void OnDisconnected_ts(const ting::Ref<Connection>& c) = 0;

	virtual bool OnDataReceived_ts(const ting::Ref<Connection>& c, const ting::Buffer<ting::u8>& d) = 0;

	virtual void OnDataSent_ts(const ting::Ref<Connection>& c, unsigned numPacketsInQueue, bool addedToQueue){}

	virtual ~Listener(){
		ASSERT(this->numTimesAdded == 0)
	}
};

}//~namespace
<commit_msg>some doxygen docs written<commit_after>/* The MIT License:

Copyright (c) 2009-2011 Ivan Gagis <igagis@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */

//Homepage: http://code.google.com/p/cliser/

/**
 * @file Connection.hpp
 * @author Ivan Gagis <igagis@gmail.com>
 * @brief Connection base class.
 * Connection base class. User is supposed to derive his own connection
 * object from it.
 */

#pragma once

#include <ting/types.hpp>
#include <ting/Array.hpp>
#include <ting/Buffer.hpp>
#include <ting/Ref.hpp>
#include <ting/Socket.hpp>
#include <ting/Thread.hpp>

namespace cliser{

//forward declarations
class ConnectionsThread;

/**
 * @brief Connection base class.
 * This class incapsulates all the data used by the cliser library to receive and send data.
 * Basically, it incapsulates the network socket and some other data.
 * Also, user can send data to remote end through a Connection object, using its Send_ts() and SendCopy_ts() methods.
 * User should subcluss the cliser::Connection object to include his own data.
 * Connection objects are created using abstract factory method of cliser::Listener interface.
 */
class Connection : public virtual ting::RefCounted{
	friend class ConnectionsThread;
	friend class ServerThread;
	friend class ClientThread;

	//This is the network data associated with Connection
	std::list<ting::Array<ting::u8> > packetQueue;
	unsigned dataSent;//number of bytes sent from first packet in the queue
	//~

	ting::TCPSocket socket;

	//NOTE: clientThread may be accessed from different threads, therefore, protect it with mutex
	ConnectionsThread *parentThread;
	ting::Mutex parentThreadMutex;

	ting::Array<ting::u8> receivedData;//Should be protected with mutex

	inline void SetHandlingThread(ConnectionsThread *thr){
		ASSERT(thr)
		ting::Mutex::Guard mutexGuard(this->parentThreadMutex);
		//Assert that client is not added to some thread already.
		ASSERT_INFO(!this->parentThread, "client's handler thread is already set")
		this->parentThread = thr;
	}

	inline void ClearHandlingThread(){
		ting::Mutex::Guard mutexGuard(this->parentThreadMutex);
		this->parentThread = 0;
	}

protected:
	ting::Mutex receivedDataMutex;

	inline Connection() :
			parentThread(0)
	{}

public:
	virtual ~Connection(){
//		TRACE(<< "Connection::" << __func__ << "(): invoked" << std::endl)
	}

	/**
	 * @brief Request disconnection.
	 * This method sends a disconnect request. As a result the
	 * cliser::Listener::OnDisconnected_ts() method will be called, this will
	 * indicate that the disconnection has actually happened.
	 * This method is thread safe (the _ts suffix tells that).
	 */
	void Disconnect_ts();

	/**
	 * @brief Request data sending to the remote end.
	 * This method requests data sending to the remote end.
	 * The cliser::Listener::OnDataSent_ts() method will be called to indicate
	 * the operation completion. Note, that possibly all the data cannot be sent at once,
	 * in that case the data is added to sending queue and will be sent when possible.
	 * When data is added to send queue the cliser::Listener::OnDataSent_ts() is also called
	 * with its arguments appropriately set, indicating that the data was not sent, but adde to
	 * sending queue.
	 * @param data - data to send.
	 */
	void Send_ts(ting::Array<ting::u8> data);

	/**
	 * @brief Request sending of data copy to the remote end.
	 * This methods works same as Send_ts() except that it copies the data passed
	 * as argument instead of taking ownership of the passed ting::Array.
	 * @param data
	 */
	void SendCopy_ts(const ting::Buffer<ting::u8>& data);

	/**
	 * @brief Get stored received data and resume listening for incoming data.
	 * This method gets stored received data if any, and resumes the listening for incoming data.
	 * The arrival of network data from the remote end is notified through
	 * cliser::Listener::OnDataReceived_ts() callback method and the user is
	 * supposed to handle the data and return true value from the method. But,
	 * if it is not possible to handle the data at current point of time the user
	 * can return false from the callback method and then the received data will be
	 * stored inside the cliser::Connection object and the listening for incoming
	 * data will be stopped for this connection. Thus, when the user is able to handle the data
	 * he should call the GetReceivedData_ts() method to get the data, and resume listening for
	 * further incoming data from the remote end.
	 * @return the valid Array with the data if there is data stored.
	 * @return invalid Array object if there is no data stored.
	 */
	ting::Array<ting::u8> GetReceivedData_ts();
};

/**
 * @brief Abstract interface to get notifications on network events.
 * This is an abstract interface which is supposed to be implemented by user.
 * The interface is used to get notifications on network events and handle them.
 * Also it is used to construct Connection objects, it has the
 * CreateConnectionObject() factory method for that.
 * Note, that methods marked with _ts suffix are suposed to be thread safe. That
 * means that the methods will be called from different threads and user should do
 * any synchronization which is needed when implementing these methods.
 */
class Listener{

	//NOTE: this numTimesAdded variable is only needed to check that
	//      the listener object has not been deleted before its owner
	//      (the object which does notifications through this listener).
	friend class cliser::ConnectionsThread;
	friend class ServerThread;
	ting::Inited<unsigned, 0> numTimesAdded;

public:

	/**
	 * @brief Create connection object.
	 * This method is called when cliser system needs a new connection object.
	 * The user is supposed to reimplement the method and return a reference to
	 * the newly created Connection object.
	 * @return ting::Ref reference to the newly created Connection object.
	 */
	virtual ting::Ref<cliser::Connection> CreateConnectionObject() = 0;

	/**
	 * @brief New connection estabilished.
	 * This callback method is called by cliser system to indicate that new
	 * connection was estabilished.
	 * The method is supposed to be thread safe.
	 * @param c - connection object of the new connection.
	 */
	virtual void OnConnected_ts(const ting::Ref<Connection>& c) = 0;

	/**
	 * @brief connection closed.
	 * This callback method is called by cliser system to indicate that a
	 * connection was closed.
	 * The method is supposed to be thread safe.
	 * @param c - connection object of the connection which was closed.
	 */
	virtual void OnDisconnected_ts(const ting::Ref<Connection>& c) = 0;

	/**
	 * @brief Data received.
	 * This callback method is called by cliser system to indicate that
	 * some network data was received from remote end.
	 * The method is supposed to be thread safe.
	 * @param c - connection object.
	 * @param d -data which was received.
	 * @return true if the data was successfully handled.
	 * @reutrn false if the data was not handled. In that case, the data will be stored in the
	 *         Connection object. Listening for new incoming data from remote end will also be stopped.
	 *         The data can later be retrieved using cliser::Connection::GetReceivedData_ts()
	 *         method which will also resume listening for new incoming data from remote end.
	 */
	virtual bool OnDataReceived_ts(const ting::Ref<Connection>& c, const ting::Buffer<ting::u8>& d) = 0;

	/**
	 * @brief Data has been sent.
	 * This callback method is called by cliser system to indicate that the data was
	 * sent or placed to sending queue. Note, that for data placed into the sending queue
	 * this method will be called twice: first, when the data is placed to the queue and
	 * second time, when the data has actually been sent to the remote end.
	 * The method is supposed to be thread safe.
     * @param c - Connection object.
     * @param numPacketsInQueue - number of packets currently stored in the sending queue of the connection object.
     * @param addedToQueue - flag indicating that the data was stored to the sending queue instead of actually sending.
     */
	virtual void OnDataSent_ts(const ting::Ref<Connection>& c, unsigned numPacketsInQueue, bool addedToQueue){}

	virtual ~Listener(){
		ASSERT(this->numTimesAdded == 0)
	}
};

}//~namespace
<|endoftext|>


<commit_before>/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. All Rights Reserved.                             */
/* Open Source Software - may be modified and shared by FRC teams. The code   */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project.                                                               */
/*----------------------------------------------------------------------------*/

#include "NetworkConnection.h"

#include "tcpsockets/TCPStream.h"
#include "raw_socket_istream.h"
#include "WireDecoder.h"
#include "WireEncoder.h"

using namespace nt;

NetworkConnection::NetworkConnection(std::unique_ptr<TCPStream> stream,
                                     Message::GetEntryTypeFunc get_entry_type)
    : m_stream(std::move(stream)),
      m_get_entry_type(get_entry_type) {
  m_active = false;
  m_proto_rev = 0x0300;
}

NetworkConnection::~NetworkConnection() { Stop(); }

void NetworkConnection::Start() {
  if (m_active) return;
  m_active = true;
  m_write_thread = std::thread(&NetworkConnection::WriteThreadMain, this);
  m_read_thread = std::thread(&NetworkConnection::ReadThreadMain, this);
}

void NetworkConnection::Stop() {
  m_active = false;
  // closing the stream so the read thread terminates
  if (m_stream) m_stream->close();
  // send a dummy outgoing message so the write thread terminates
  m_outgoing.push(Outgoing{Message::KeepAlive()});
  if (m_write_thread.joinable()) m_write_thread.join();
  if (m_read_thread.joinable()) m_read_thread.join();
}

void NetworkConnection::ReadThreadMain() {
  raw_socket_istream is(*m_stream);
  WireDecoder decoder(is, m_proto_rev);

  while (m_active) {
    if (!m_stream)
      break;
    decoder.set_proto_rev(m_proto_rev);
    decoder.Reset();
    auto msg = Message::Read(decoder, m_get_entry_type);
    if (!msg) {
      // terminate connection on bad message
      if (m_stream) m_stream->close();
      break;
    }
    m_incoming.push(msg);
  }
  m_incoming.push(nullptr);  // notify anyone waiting that we disconnected
  m_active = false;
}

void NetworkConnection::WriteThreadMain() {
  WireEncoder encoder(m_proto_rev);

  while (m_active) {
    auto msgs = m_outgoing.pop();
    if (!m_active) break;
    encoder.set_proto_rev(m_proto_rev);
    encoder.Reset();
    for (auto& msg : msgs) msg->Write(encoder);
    TCPStream::Error err;
    if (!m_stream) break;
    if (m_stream->send(encoder.data(), encoder.size(), &err) == 0) break;
  }
  m_active = false;
}
<commit_msg>NetworkConnection: Prefer emplace to push.<commit_after>/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015. All Rights Reserved.                             */
/* Open Source Software - may be modified and shared by FRC teams. The code   */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project.                                                               */
/*----------------------------------------------------------------------------*/

#include "NetworkConnection.h"

#include "tcpsockets/TCPStream.h"
#include "raw_socket_istream.h"
#include "WireDecoder.h"
#include "WireEncoder.h"

using namespace nt;

NetworkConnection::NetworkConnection(std::unique_ptr<TCPStream> stream,
                                     Message::GetEntryTypeFunc get_entry_type)
    : m_stream(std::move(stream)),
      m_get_entry_type(get_entry_type) {
  m_active = false;
  m_proto_rev = 0x0300;
}

NetworkConnection::~NetworkConnection() { Stop(); }

void NetworkConnection::Start() {
  if (m_active) return;
  m_active = true;
  m_write_thread = std::thread(&NetworkConnection::WriteThreadMain, this);
  m_read_thread = std::thread(&NetworkConnection::ReadThreadMain, this);
}

void NetworkConnection::Stop() {
  m_active = false;
  // closing the stream so the read thread terminates
  if (m_stream) m_stream->close();
  // send a dummy outgoing message so the write thread terminates
  m_outgoing.push(Outgoing{Message::KeepAlive()});
  if (m_write_thread.joinable()) m_write_thread.join();
  if (m_read_thread.joinable()) m_read_thread.join();
}

void NetworkConnection::ReadThreadMain() {
  raw_socket_istream is(*m_stream);
  WireDecoder decoder(is, m_proto_rev);

  while (m_active) {
    if (!m_stream)
      break;
    decoder.set_proto_rev(m_proto_rev);
    decoder.Reset();
    auto msg = Message::Read(decoder, m_get_entry_type);
    if (!msg) {
      // terminate connection on bad message
      if (m_stream) m_stream->close();
      break;
    }
    m_incoming.emplace(std::move(msg));
  }
  m_incoming.emplace(nullptr);  // notify anyone waiting that we disconnected
  m_active = false;
}

void NetworkConnection::WriteThreadMain() {
  WireEncoder encoder(m_proto_rev);

  while (m_active) {
    auto msgs = m_outgoing.pop();
    if (!m_active) break;
    encoder.set_proto_rev(m_proto_rev);
    encoder.Reset();
    for (auto& msg : msgs) msg->Write(encoder);
    TCPStream::Error err;
    if (!m_stream) break;
    if (m_stream->send(encoder.data(), encoder.size(), &err) == 0) break;
  }
  m_active = false;
}
<|endoftext|>


<commit_before>
/*
 * Copyright (C) 2015 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <boost/test/unit_test.hpp>

#include <seastar/util/defer.hh>

#include <seastar/testing/test_case.hh>
#include "message/messaging_service.hh"
#include "gms/failure_detector.hh"
#include "gms/gossiper.hh"
#include "gms/feature_service.hh"
#include <seastar/core/seastar.hh>
#include "service/storage_service.hh"
#include <seastar/core/distributed.hh>
#include <seastar/core/abort_source.hh>
#include "database.hh"
#include "db/system_distributed_keyspace.hh"
#include "db/config.hh"

namespace db::view {
class view_update_generator;
}

SEASTAR_TEST_CASE(test_boot_shutdown){
    return seastar::async([] {
        distributed<database> db;
        database_config dbcfg;
        auto cfg = std::make_unique<db::config>();
        sharded<service::migration_notifier> mm_notif;
        sharded<abort_source> abort_sources;
        sharded<auth::service> auth_service;
        sharded<db::system_distributed_keyspace> sys_dist_ks;
        sharded<db::view::view_update_generator> view_update_generator;
        utils::fb_utilities::set_broadcast_address(gms::inet_address("127.0.0.1"));
        sharded<gms::feature_service> feature_service;
        sharded<locator::token_metadata> token_metadata;

        token_metadata.start().get();
        auto stop_token_mgr = defer([&token_metadata] { token_metadata.stop().get(); });

        mm_notif.start().get();
        auto stop_mm_notif = defer([&mm_notif] { mm_notif.stop().get(); });

        abort_sources.start().get();
        auto stop_abort_sources = defer([&] { abort_sources.stop().get(); });

        feature_service.start(gms::feature_config_from_db_config(*cfg)).get();
        auto stop_feature_service = defer([&] { feature_service.stop().get(); });

        locator::i_endpoint_snitch::create_snitch("SimpleSnitch").get();
        auto stop_snitch = defer([&] { locator::i_endpoint_snitch::stop_snitch().get(); });

        netw::get_messaging_service().start(gms::inet_address("127.0.0.1"), 7000).get();
        auto stop_messaging_service = defer([&] { netw::get_messaging_service().stop().get(); });

        gms::get_gossiper().start(std::ref(abort_sources), std::ref(feature_service), std::ref(token_metadata), std::ref(*cfg)).get();
        auto stop_gossiper = defer([&] { gms::get_gossiper().stop().get(); });

        service::storage_service_config sscfg;
        sscfg.available_memory =  memory::stats().total_memory();

        service::get_storage_service().start(std::ref(abort_sources), std::ref(db), std::ref(gms::get_gossiper()), std::ref(auth_service), std::ref(sys_dist_ks), std::ref(view_update_generator), std::ref(feature_service), sscfg, std::ref(mm_notif), std::ref(token_metadata), true).get();
        auto stop_ss = defer([&] { service::get_storage_service().stop().get(); });

        db.start(std::ref(*cfg), dbcfg, std::ref(mm_notif), std::ref(feature_service), std::ref(token_metadata)).get();
        auto stop_db = defer([&] { db.stop().get(); });
        auto stop_database_d = defer([&db] {
            stop_database(db).get();
        });

    });
}
<commit_msg>gossip_test: start the compaction manager explicitly<commit_after>
/*
 * Copyright (C) 2015 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <boost/test/unit_test.hpp>

#include <seastar/util/defer.hh>

#include <seastar/testing/test_case.hh>
#include "message/messaging_service.hh"
#include "gms/failure_detector.hh"
#include "gms/gossiper.hh"
#include "gms/feature_service.hh"
#include <seastar/core/seastar.hh>
#include "service/storage_service.hh"
#include <seastar/core/distributed.hh>
#include <seastar/core/abort_source.hh>
#include "database.hh"
#include "db/system_distributed_keyspace.hh"
#include "db/config.hh"
#include "sstables/compaction_manager.hh"

namespace db::view {
class view_update_generator;
}

SEASTAR_TEST_CASE(test_boot_shutdown){
    return seastar::async([] {
        distributed<database> db;
        database_config dbcfg;
        auto cfg = std::make_unique<db::config>();
        sharded<service::migration_notifier> mm_notif;
        sharded<abort_source> abort_sources;
        sharded<auth::service> auth_service;
        sharded<db::system_distributed_keyspace> sys_dist_ks;
        sharded<db::view::view_update_generator> view_update_generator;
        utils::fb_utilities::set_broadcast_address(gms::inet_address("127.0.0.1"));
        sharded<gms::feature_service> feature_service;
        sharded<locator::token_metadata> token_metadata;

        token_metadata.start().get();
        auto stop_token_mgr = defer([&token_metadata] { token_metadata.stop().get(); });

        mm_notif.start().get();
        auto stop_mm_notif = defer([&mm_notif] { mm_notif.stop().get(); });

        abort_sources.start().get();
        auto stop_abort_sources = defer([&] { abort_sources.stop().get(); });

        feature_service.start(gms::feature_config_from_db_config(*cfg)).get();
        auto stop_feature_service = defer([&] { feature_service.stop().get(); });

        locator::i_endpoint_snitch::create_snitch("SimpleSnitch").get();
        auto stop_snitch = defer([&] { locator::i_endpoint_snitch::stop_snitch().get(); });

        netw::get_messaging_service().start(gms::inet_address("127.0.0.1"), 7000).get();
        auto stop_messaging_service = defer([&] { netw::get_messaging_service().stop().get(); });

        gms::get_gossiper().start(std::ref(abort_sources), std::ref(feature_service), std::ref(token_metadata), std::ref(*cfg)).get();
        auto stop_gossiper = defer([&] { gms::get_gossiper().stop().get(); });

        service::storage_service_config sscfg;
        sscfg.available_memory =  memory::stats().total_memory();

        service::get_storage_service().start(std::ref(abort_sources), std::ref(db), std::ref(gms::get_gossiper()), std::ref(auth_service), std::ref(sys_dist_ks), std::ref(view_update_generator), std::ref(feature_service), sscfg, std::ref(mm_notif), std::ref(token_metadata), true).get();
        auto stop_ss = defer([&] { service::get_storage_service().stop().get(); });

        db.start(std::ref(*cfg), dbcfg, std::ref(mm_notif), std::ref(feature_service), std::ref(token_metadata)).get();
        db.invoke_on_all([] (database& db) {
            db.get_compaction_manager().start();
        }).get();

        auto stop_db = defer([&] { db.stop().get(); });
        auto stop_database_d = defer([&db] {
            stop_database(db).get();
        });

    });
}
<|endoftext|>


<commit_before>/*
 * AsyncClient.hpp
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#ifndef CORE_HTTP_ASYNC_CLIENT_HPP
#define CORE_HTTP_ASYNC_CLIENT_HPP

#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <boost/enable_shared_from_this.hpp>

#include <boost/asio/write.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/placeholders.hpp>
#include <boost/asio/streambuf.hpp>
#include <boost/asio/read.hpp>
#include <boost/asio/read_until.hpp>
#include <boost/asio/deadline_timer.hpp>

#include <core/Error.hpp>
#include <core/Log.hpp>
#include <core/system/System.hpp>

#include <core/http/Request.hpp>
#include <core/http/Response.hpp>
#include <core/http/ResponseParser.hpp>
#include <core/http/Socket.hpp>
#include <core/http/SocketUtils.hpp>
#include <core/http/ConnectionRetryProfile.hpp>

// special version of unexpected exception handler which makes
// sure to call the user's ErrorHandler
#define CATCH_UNEXPECTED_ASYNC_CLIENT_EXCEPTION \
   catch(const std::exception& e) \
   { \
      handleUnexpectedError(std::string("Unexpected exception: ") + \
                            e.what(), ERROR_LOCATION) ;  \
   } \
   catch(...) \
   { \
      handleUnexpectedError("Unknown exception", ERROR_LOCATION); \
   }

namespace rstudio {
namespace core {
namespace http {

typedef boost::function<void(const http::Response&)> ResponseHandler;
typedef boost::function<void(const core::Error&)> ErrorHandler;

template <typename SocketService>
class AsyncClient :
   public boost::enable_shared_from_this<AsyncClient<SocketService> >,
   public Socket,
   boost::noncopyable
{
public:
   AsyncClient(boost::asio::io_service& ioService,
               bool logToStderr = false)
      : ioService_(ioService),
        connectionRetryContext_(ioService),
        logToStderr_(logToStderr)
   {
   }

   virtual ~AsyncClient()
   {
   }

   // populate the request before calling execute
   http::Request& request() { return request_; }

   // set (optional) connection retry profile. must do this prior
   // to calling execute
   void setConnectionRetryProfile(
           const http::ConnectionRetryProfile& connectionRetryProfile)
   {
      connectionRetryContext_.profile = connectionRetryProfile;
   }

   // execute the async client
   void execute(const ResponseHandler& responseHandler,
                const ErrorHandler& errorHandler)
   {
      // set handlers
      responseHandler_ = responseHandler;
      errorHandler_ = errorHandler;

      // connect and write request (implmented in a protocol
      // specific manner by subclassees)
      connectAndWriteRequest();
   }

   // if an embedder of this class calls close() on AsyncClient in it's
   // destructor (for more rigorous cleanup) then it's possible that the
   // onError handler will still be called as a result of the socket close.
   // the callback might then be interacting with a C++ object that has
   // already been deleted. for this case (which does occur in the
   // desktop::NetworkReply class) we provide a method that disables
   // any pending handlers
   void disableHandlers()
   {
      responseHandler_ = ResponseHandler();
      errorHandler_ = ErrorHandler();
   }

   // satisfy lower-level http::Socket interface (used when the client
   // is upgraded to a websocket connection and no longer conforms to
   // the request/response protocol used by the class in the ordinary
   // course of business)

   virtual void asyncReadSome(boost::asio::mutable_buffers_1 buffer,
                              Handler handler)
   {
      socket().async_read_some(buffer, handler);
   }

   virtual void asyncWrite(
                     const std::vector<boost::asio::const_buffer>& buffers,
                     Handler handler)
   {
      boost::asio::async_write(socket(), buffers, handler);
   }

   void close()
   {
      Error error = closeSocket(socket().lowest_layer());
      if (error && !core::http::isConnectionTerminatedError(error))
         logError(error);
   }

protected:

   boost::asio::io_service& ioService() { return ioService_; }

   virtual SocketService& socket() = 0;

   void handleConnectionError(const Error& connectionError)
   {
      // retry if necessary, otherwise just forward the error to
      // customary error handling scheme

      Error otherError;
      if (!retryConnectionIfRequired(connectionError, &otherError))
      {
         if (otherError)
            handleError(otherError);
         else
            handleError(connectionError);
      }
   }

   // asynchronously write the request (called by subclasses after
   // they finish connecting)
   void writeRequest()
   {
      // write
      boost::asio::async_write(
          socket(),
          request_.toBuffers(Header::connectionClose()),
          boost::bind(
               &AsyncClient<SocketService>::handleWrite,
               AsyncClient<SocketService>::shared_from_this(),
               boost::asio::placeholders::error)
      );
   }

   void handleError(const Error& error)
   {
      // close the socket
      close();

      if (errorHandler_)
         errorHandler_(error);
   }

   void handleErrorCode(const boost::system::error_code& ec,
                        const ErrorLocation& location)
   {
      handleError(Error(ec, location));
   }

   void handleUnexpectedError(const std::string& description,
                              const ErrorLocation& location)
   {
      Error error = systemError(boost::system::errc::state_not_recoverable,
                                description,
                                location);
      handleError(error);
   }

private:

   virtual void connectAndWriteRequest() = 0;

   bool retryConnectionIfRequired(const Error& connectionError,
                                  Error* pOtherError)
   {
      // retry if this is a connection unavailable error and the
      // caller has provided a connection retry profile
      if (http::isConnectionUnavailableError(connectionError) &&
          !connectionRetryContext_.profile.empty())
      {
         // if this is our first retry then set our stop trying time
         bool firstAttempt = false;
         if (connectionRetryContext_.stopTryingTime.is_not_a_date_time())
         {
            connectionRetryContext_.stopTryingTime =
                  boost::posix_time::microsec_clock::universal_time() +
                  connectionRetryContext_.profile.maxWait;

            firstAttempt = true;
         }

         // call recovery function if we have it
         if (connectionRetryContext_.profile.recoveryFunction)
         {
            Error error = connectionRetryContext_.profile
                                   .recoveryFunction(request_, firstAttempt);
            if (error)
            {
               *pOtherError = error;
               return false;
            }
         }

         // if we aren't alrady past the maximum wait time then
         // wait the appropriate interval and attempt connection again
         if (boost::posix_time::microsec_clock::universal_time() <
             connectionRetryContext_.stopTryingTime)
         {
            return scheduleRetry(); // continuation
         }
         else // otherwise we've waited long enough, bail and
              // perform normal error handling
         {
            return false;
         }
      }
      else // not an error subject to retrying or no retry profile provided
      {
         return false;
      }
   }

   bool scheduleRetry()
   {
      // set expiration
      boost::system::error_code ec;
      connectionRetryContext_.retryTimer.expires_from_now(
                  connectionRetryContext_.profile.retryInterval,
                  ec);

      // attempt to schedule retry timer (should always succeed but
      // include error check to be paranoid/robust)
      if (!ec)
      {
         connectionRetryContext_.retryTimer.async_wait(boost::bind(
               &AsyncClient<SocketService>::handleConnectionRetryTimer,
               AsyncClient<SocketService>::shared_from_this(),
               boost::asio::placeholders::error));

         return true;
      }
      else
      {
         logError(Error(ec, ERROR_LOCATION));
         return false;
      }
   }

   void handleConnectionRetryTimer(const boost::system::error_code& ec)
   {
      try
      {
         if (!ec)
         {
            connectAndWriteRequest();
         }
         else
         {
            handleErrorCode(ec, ERROR_LOCATION);
         }
      }
      CATCH_UNEXPECTED_ASYNC_CLIENT_EXCEPTION
   }

   void handleWrite(const boost::system::error_code& ec)
   {
      try
      {
         if (!ec)
         {
            // initiate async read of the first line of the response
            boost::asio::async_read_until(
              socket(),
              responseBuffer_,
              "\r\n",
              boost::bind(&AsyncClient<SocketService>::handleReadStatusLine,
                          AsyncClient<SocketService>::shared_from_this(),
                          boost::asio::placeholders::error));
         }
         else
         {
            handleErrorCode(ec, ERROR_LOCATION);
         }
      }
      CATCH_UNEXPECTED_ASYNC_CLIENT_EXCEPTION
   }

   void handleReadStatusLine(const boost::system::error_code& ec)
   {
      try
      {
         if (!ec)
         {
            // parase status line
            Error error = ResponseParser::parseStatusLine(&responseBuffer_,
                                                          &response_);
            if (error)
            {
               handleError(error);
            }
            else
            {
               // initiate async read of the headers
               boost::asio::async_read_until(
                 socket(),
                 responseBuffer_,
                 "\r\n\r\n",
                 boost::bind(&AsyncClient<SocketService>::handleReadHeaders,
                             AsyncClient<SocketService>::shared_from_this(),
                             boost::asio::placeholders::error));
            }
         }
         else
         {
            handleErrorCode(ec, ERROR_LOCATION);
         }
      }
      CATCH_UNEXPECTED_ASYNC_CLIENT_EXCEPTION
   }

   void readSomeContent()
   {
      // provide a hook for subclasses to force termination of
      // content reads (this is needed for named pipes on windows,
      // where the client disconnecting from the server is part
      // of the normal pipe shutdown sequence). without this
      // the subsequent call to handleReadContent will perform
      // the close and respond when it gets a shutdown error (as
      // a result of the server shutting down)
      if (stopReadingAndRespond())
      {
         closeAndRespond();
         return;
      }

      boost::asio::async_read(
         socket(),
         responseBuffer_,
         boost::asio::transfer_at_least(1),
         boost::bind(&AsyncClient<SocketService>::handleReadContent,
                     AsyncClient<SocketService>::shared_from_this(),
                     boost::asio::placeholders::error));
   }

   virtual bool stopReadingAndRespond()
   {
      return false;
   }

   virtual bool keepConnectionAlive()
   {
      return false;
   }

   void handleReadHeaders(const boost::system::error_code& ec)
   {
      try
      {
         if (!ec)
         {
            // parse headers
            ResponseParser::parseHeaders(&responseBuffer_, &response_);

            // append any lefover buffer contents to the body
            if (responseBuffer_.size() > 0)
               ResponseParser::appendToBody(&responseBuffer_, &response_);

            // start reading content
            readSomeContent();
         }
         else
         {
            handleErrorCode(ec, ERROR_LOCATION);
         }
      }
      CATCH_UNEXPECTED_ASYNC_CLIENT_EXCEPTION
   }

   void handleReadContent(const boost::system::error_code& ec)
   {
      try
      {
         if (!ec)
         {
            // copy content
            ResponseParser::appendToBody(&responseBuffer_, &response_);

            // continue reading content
            readSomeContent();
         }
         else if (ec == boost::asio::error::eof ||
                  isShutdownError(ec))
         {
            closeAndRespond();
         }
         else
         {
            handleErrorCode(ec, ERROR_LOCATION);
         }
      }
      CATCH_UNEXPECTED_ASYNC_CLIENT_EXCEPTION
   }

   virtual bool isShutdownError(const boost::system::error_code& ec)
   {
      return false;
   }

   void closeAndRespond()
   {
      if (!keepConnectionAlive())
         close();

      if (responseHandler_)
         responseHandler_(response_);
   }

   void logError(const Error& error) const
   {
      if (logToStderr_)
      {
         std::cerr << error << std::endl;
      }
      else
      {
         LOG_ERROR(error);
      }
   }

// struct and instance variable to track connection retry state
private:
   struct ConnectionRetryContext
   {
      ConnectionRetryContext(boost::asio::io_service& ioService)
         : stopTryingTime(boost::posix_time::not_a_date_time),
           retryTimer(ioService)
      {
      }

      http::ConnectionRetryProfile profile;
      boost::posix_time::ptime stopTryingTime;
      boost::asio::deadline_timer retryTimer;
   };

protected:
   http::Response response_;

private:
   boost::asio::io_service& ioService_;
   ConnectionRetryContext connectionRetryContext_;
   bool logToStderr_;
   ResponseHandler responseHandler_;
   ErrorHandler errorHandler_;
   http::Request request_;
   boost::asio::streambuf responseBuffer_;
};

} // namespace http
} // namespace core
} // namespace rstudio

#endif // CORE_HTTP_ASYNC_CLIENT_HPP

<commit_msg>don't override websocket upgrade connection header when writing request<commit_after>/*
 * AsyncClient.hpp
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#ifndef CORE_HTTP_ASYNC_CLIENT_HPP
#define CORE_HTTP_ASYNC_CLIENT_HPP

#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <boost/enable_shared_from_this.hpp>

#include <boost/asio/write.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/placeholders.hpp>
#include <boost/asio/streambuf.hpp>
#include <boost/asio/read.hpp>
#include <boost/asio/read_until.hpp>
#include <boost/asio/deadline_timer.hpp>

#include <boost/algorithm/string/predicate.hpp>

#include <core/Error.hpp>
#include <core/Log.hpp>
#include <core/system/System.hpp>

#include <core/http/Request.hpp>
#include <core/http/Response.hpp>
#include <core/http/ResponseParser.hpp>
#include <core/http/Socket.hpp>
#include <core/http/SocketUtils.hpp>
#include <core/http/ConnectionRetryProfile.hpp>

// special version of unexpected exception handler which makes
// sure to call the user's ErrorHandler
#define CATCH_UNEXPECTED_ASYNC_CLIENT_EXCEPTION \
   catch(const std::exception& e) \
   { \
      handleUnexpectedError(std::string("Unexpected exception: ") + \
                            e.what(), ERROR_LOCATION) ;  \
   } \
   catch(...) \
   { \
      handleUnexpectedError("Unknown exception", ERROR_LOCATION); \
   }

namespace rstudio {
namespace core {
namespace http {

typedef boost::function<void(const http::Response&)> ResponseHandler;
typedef boost::function<void(const core::Error&)> ErrorHandler;

template <typename SocketService>
class AsyncClient :
   public boost::enable_shared_from_this<AsyncClient<SocketService> >,
   public Socket,
   boost::noncopyable
{
public:
   AsyncClient(boost::asio::io_service& ioService,
               bool logToStderr = false)
      : ioService_(ioService),
        connectionRetryContext_(ioService),
        logToStderr_(logToStderr)
   {
   }

   virtual ~AsyncClient()
   {
   }

   // populate the request before calling execute
   http::Request& request() { return request_; }

   // set (optional) connection retry profile. must do this prior
   // to calling execute
   void setConnectionRetryProfile(
           const http::ConnectionRetryProfile& connectionRetryProfile)
   {
      connectionRetryContext_.profile = connectionRetryProfile;
   }

   // execute the async client
   void execute(const ResponseHandler& responseHandler,
                const ErrorHandler& errorHandler)
   {
      // set handlers
      responseHandler_ = responseHandler;
      errorHandler_ = errorHandler;

      // connect and write request (implmented in a protocol
      // specific manner by subclassees)
      connectAndWriteRequest();
   }

   // if an embedder of this class calls close() on AsyncClient in it's
   // destructor (for more rigorous cleanup) then it's possible that the
   // onError handler will still be called as a result of the socket close.
   // the callback might then be interacting with a C++ object that has
   // already been deleted. for this case (which does occur in the
   // desktop::NetworkReply class) we provide a method that disables
   // any pending handlers
   void disableHandlers()
   {
      responseHandler_ = ResponseHandler();
      errorHandler_ = ErrorHandler();
   }

   // satisfy lower-level http::Socket interface (used when the client
   // is upgraded to a websocket connection and no longer conforms to
   // the request/response protocol used by the class in the ordinary
   // course of business)

   virtual void asyncReadSome(boost::asio::mutable_buffers_1 buffer,
                              Handler handler)
   {
      socket().async_read_some(buffer, handler);
   }

   virtual void asyncWrite(
                     const std::vector<boost::asio::const_buffer>& buffers,
                     Handler handler)
   {
      boost::asio::async_write(socket(), buffers, handler);
   }

   void close()
   {
      Error error = closeSocket(socket().lowest_layer());
      if (error && !core::http::isConnectionTerminatedError(error))
         logError(error);
   }

protected:

   boost::asio::io_service& ioService() { return ioService_; }

   virtual SocketService& socket() = 0;

   void handleConnectionError(const Error& connectionError)
   {
      // retry if necessary, otherwise just forward the error to
      // customary error handling scheme

      Error otherError;
      if (!retryConnectionIfRequired(connectionError, &otherError))
      {
         if (otherError)
            handleError(otherError);
         else
            handleError(connectionError);
      }
   }

   // asynchronously write the request (called by subclasses after
   // they finish connecting)
   void writeRequest()
   {
      // specify closing of the connection after the request unless this is
      // an attempt to upgrade to websockets
      Header overrideHeader;
      if (!boost::algorithm::iequals(request_.headerValue("Connection"),
                                     "Upgrade"))
      {
         overrideHeader = Header::connectionClose();
      }

      // write
      boost::asio::async_write(
          socket(),
          request_.toBuffers(overrideHeader),
          boost::bind(
               &AsyncClient<SocketService>::handleWrite,
               AsyncClient<SocketService>::shared_from_this(),
               boost::asio::placeholders::error)
      );
   }

   void handleError(const Error& error)
   {
      // close the socket
      close();

      if (errorHandler_)
         errorHandler_(error);
   }

   void handleErrorCode(const boost::system::error_code& ec,
                        const ErrorLocation& location)
   {
      handleError(Error(ec, location));
   }

   void handleUnexpectedError(const std::string& description,
                              const ErrorLocation& location)
   {
      Error error = systemError(boost::system::errc::state_not_recoverable,
                                description,
                                location);
      handleError(error);
   }

private:

   virtual void connectAndWriteRequest() = 0;

   bool retryConnectionIfRequired(const Error& connectionError,
                                  Error* pOtherError)
   {
      // retry if this is a connection unavailable error and the
      // caller has provided a connection retry profile
      if (http::isConnectionUnavailableError(connectionError) &&
          !connectionRetryContext_.profile.empty())
      {
         // if this is our first retry then set our stop trying time
         bool firstAttempt = false;
         if (connectionRetryContext_.stopTryingTime.is_not_a_date_time())
         {
            connectionRetryContext_.stopTryingTime =
                  boost::posix_time::microsec_clock::universal_time() +
                  connectionRetryContext_.profile.maxWait;

            firstAttempt = true;
         }

         // call recovery function if we have it
         if (connectionRetryContext_.profile.recoveryFunction)
         {
            Error error = connectionRetryContext_.profile
                                   .recoveryFunction(request_, firstAttempt);
            if (error)
            {
               *pOtherError = error;
               return false;
            }
         }

         // if we aren't alrady past the maximum wait time then
         // wait the appropriate interval and attempt connection again
         if (boost::posix_time::microsec_clock::universal_time() <
             connectionRetryContext_.stopTryingTime)
         {
            return scheduleRetry(); // continuation
         }
         else // otherwise we've waited long enough, bail and
              // perform normal error handling
         {
            return false;
         }
      }
      else // not an error subject to retrying or no retry profile provided
      {
         return false;
      }
   }

   bool scheduleRetry()
   {
      // set expiration
      boost::system::error_code ec;
      connectionRetryContext_.retryTimer.expires_from_now(
                  connectionRetryContext_.profile.retryInterval,
                  ec);

      // attempt to schedule retry timer (should always succeed but
      // include error check to be paranoid/robust)
      if (!ec)
      {
         connectionRetryContext_.retryTimer.async_wait(boost::bind(
               &AsyncClient<SocketService>::handleConnectionRetryTimer,
               AsyncClient<SocketService>::shared_from_this(),
               boost::asio::placeholders::error));

         return true;
      }
      else
      {
         logError(Error(ec, ERROR_LOCATION));
         return false;
      }
   }

   void handleConnectionRetryTimer(const boost::system::error_code& ec)
   {
      try
      {
         if (!ec)
         {
            connectAndWriteRequest();
         }
         else
         {
            handleErrorCode(ec, ERROR_LOCATION);
         }
      }
      CATCH_UNEXPECTED_ASYNC_CLIENT_EXCEPTION
   }

   void handleWrite(const boost::system::error_code& ec)
   {
      try
      {
         if (!ec)
         {
            // initiate async read of the first line of the response
            boost::asio::async_read_until(
              socket(),
              responseBuffer_,
              "\r\n",
              boost::bind(&AsyncClient<SocketService>::handleReadStatusLine,
                          AsyncClient<SocketService>::shared_from_this(),
                          boost::asio::placeholders::error));
         }
         else
         {
            handleErrorCode(ec, ERROR_LOCATION);
         }
      }
      CATCH_UNEXPECTED_ASYNC_CLIENT_EXCEPTION
   }

   void handleReadStatusLine(const boost::system::error_code& ec)
   {
      try
      {
         if (!ec)
         {
            // parase status line
            Error error = ResponseParser::parseStatusLine(&responseBuffer_,
                                                          &response_);
            if (error)
            {
               handleError(error);
            }
            else
            {
               // initiate async read of the headers
               boost::asio::async_read_until(
                 socket(),
                 responseBuffer_,
                 "\r\n\r\n",
                 boost::bind(&AsyncClient<SocketService>::handleReadHeaders,
                             AsyncClient<SocketService>::shared_from_this(),
                             boost::asio::placeholders::error));
            }
         }
         else
         {
            handleErrorCode(ec, ERROR_LOCATION);
         }
      }
      CATCH_UNEXPECTED_ASYNC_CLIENT_EXCEPTION
   }

   void readSomeContent()
   {
      // provide a hook for subclasses to force termination of
      // content reads (this is needed for named pipes on windows,
      // where the client disconnecting from the server is part
      // of the normal pipe shutdown sequence). without this
      // the subsequent call to handleReadContent will perform
      // the close and respond when it gets a shutdown error (as
      // a result of the server shutting down)
      if (stopReadingAndRespond())
      {
         closeAndRespond();
         return;
      }

      boost::asio::async_read(
         socket(),
         responseBuffer_,
         boost::asio::transfer_at_least(1),
         boost::bind(&AsyncClient<SocketService>::handleReadContent,
                     AsyncClient<SocketService>::shared_from_this(),
                     boost::asio::placeholders::error));
   }

   virtual bool stopReadingAndRespond()
   {
      return false;
   }

   virtual bool keepConnectionAlive()
   {
      return false;
   }

   void handleReadHeaders(const boost::system::error_code& ec)
   {
      try
      {
         if (!ec)
         {
            // parse headers
            ResponseParser::parseHeaders(&responseBuffer_, &response_);

            // append any lefover buffer contents to the body
            if (responseBuffer_.size() > 0)
               ResponseParser::appendToBody(&responseBuffer_, &response_);

            // start reading content
            readSomeContent();
         }
         else
         {
            handleErrorCode(ec, ERROR_LOCATION);
         }
      }
      CATCH_UNEXPECTED_ASYNC_CLIENT_EXCEPTION
   }

   void handleReadContent(const boost::system::error_code& ec)
   {
      try
      {
         if (!ec)
         {
            // copy content
            ResponseParser::appendToBody(&responseBuffer_, &response_);

            // continue reading content
            readSomeContent();
         }
         else if (ec == boost::asio::error::eof ||
                  isShutdownError(ec))
         {
            closeAndRespond();
         }
         else
         {
            handleErrorCode(ec, ERROR_LOCATION);
         }
      }
      CATCH_UNEXPECTED_ASYNC_CLIENT_EXCEPTION
   }

   virtual bool isShutdownError(const boost::system::error_code& ec)
   {
      return false;
   }

   void closeAndRespond()
   {
      if (!keepConnectionAlive())
         close();

      if (responseHandler_)
         responseHandler_(response_);
   }

   void logError(const Error& error) const
   {
      if (logToStderr_)
      {
         std::cerr << error << std::endl;
      }
      else
      {
         LOG_ERROR(error);
      }
   }

// struct and instance variable to track connection retry state
private:
   struct ConnectionRetryContext
   {
      ConnectionRetryContext(boost::asio::io_service& ioService)
         : stopTryingTime(boost::posix_time::not_a_date_time),
           retryTimer(ioService)
      {
      }

      http::ConnectionRetryProfile profile;
      boost::posix_time::ptime stopTryingTime;
      boost::asio::deadline_timer retryTimer;
   };

protected:
   http::Response response_;

private:
   boost::asio::io_service& ioService_;
   ConnectionRetryContext connectionRetryContext_;
   bool logToStderr_;
   ResponseHandler responseHandler_;
   ErrorHandler errorHandler_;
   http::Request request_;
   boost::asio::streambuf responseBuffer_;
};

} // namespace http
} // namespace core
} // namespace rstudio

#endif // CORE_HTTP_ASYNC_CLIENT_HPP

<|endoftext|>