Blob Blame Raw

#include <cstring>

#include <chrono>
#include <thread>
#include <mutex>

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>

#include "protocol.h"
#include "server.h"


#define DebugMSG ShowDebugMSG


Server::Server():
	connFirst(), connLast()
	{ }


Server::~Server()
	{ destroy();} 


bool Server::open(Protocol &protocol, void *address, size_t addressSize)
	{ OpenLock lock(*this, protocol, address, addressSize); return lock.success; }


void Server::handleDisconnect(Connection &connection, bool error) {
	onDisconnect(&connection, error);
	if (!connFirst && stateLocal == STATE_CLOSING_CONNECTIONS) {
		DebugMSG("to closing");
		state = stateLocal = STATE_CLOSING;
		stateProtector.wait();
		onClose(error);
		enqueue();
	}
}


void Server::handleState() {
	if (stateLocal == STATE_NONE) {
		DebugMSG("none");
		int sockId = -1;
		if (addressSize >= sizeof(sa_family_t)) {
			struct sockaddr *addr = (struct sockaddr*)address;
			sockId = ::socket(addr->sa_family, SOCK_STREAM, 0);
			if (sockId >= 0) {
				initSocket(sockId);
				if ( 0 != ::bind(sockId, addr, (int)addressSize)
					|| 0 != ::listen(sockId, 128) )
				{
					DebugMSG("cannot bind/listen");
					::close(sockId);
					sockId = -1;
				}
			} else {
				DebugMSG("cannot create socket");
			}
		}
		
		if (sockId >= 0) {
			DebugMSG("to open");
			prev = protocol->sockLast;
			(prev ? prev->next : protocol->sockFirst) = this;
			this->sockId = sockId;
			
			updateEvents(true, false);
			
			state = stateLocal = STATE_OPEN;
			stateProtector.wait();
			onOpen(address, addressSize);
		} else {
			DebugMSG("opening error -> to closed");
			onOpeningError();
			state = STATE_CLOSED;
			while(closeWaiters > 0)
				{ closeCondition.notify_all(); std::this_thread::yield(); }
			state = STATE_FINISHED;
			DebugMSG("opening error -> finished");
		}
	} else {
		State sw = stateWanted;
		if ( stateLocal == STATE_OPEN
		  && sw == STATE_CLOSE_REQ )
		{
			DebugMSG("to close req");
			for(Connection *conn = connFirst; conn; conn = conn->srvNext)
				conn->closeReq();
			updateEvents(false, false);
			state = stateLocal = STATE_CLOSE_REQ;
			stateProtector.wait();
			onCloseReqested();
		}
		
		if ( stateLocal >= STATE_OPEN
		  && stateLocal <= STATE_CLOSE_REQ
		  && sw == STATE_CLOSING )
		{
			DebugMSG("to closing connections");
			updateEvents(false, false);
			if (connFirst) {
				for(Connection *conn = connFirst; conn; conn = conn->srvNext)
					conn->close(true);
				state = stateLocal = STATE_CLOSING_CONNECTIONS;
				stateProtector.wait();
			} else {
				DebugMSG("to closing");
				state = stateLocal = STATE_CLOSING;
				stateProtector.wait();
				onClose(error);
			}
		}
		
		if ( stateLocal == STATE_CLOSING && !enqueued ) {
			DebugMSG("to closed");
			if (sockId >= 0) {
				struct epoll_event event = {};
				epoll_ctl(protocol->epollFd, EPOLL_CTL_DEL, sockId, &event);
				::close(sockId);
				sockId = -1;
			}
			events = 0xffffffff;
			(prev ? prev->next : protocol->sockFirst) = next;
			(next ? next->prev : protocol->sockLast ) = prev;
			protocol = nullptr;
			memset(this->address, 0, sizeof(address));
			addressSize = 0;
			stateWanted = STATE_NONE;
			stateLocal = STATE_NONE;
			DebugMSG("closeWaiters");
			state = STATE_CLOSED;
			while(closeWaiters > 0)
				{ closeCondition.notify_all(); std::this_thread::yield(); }
			state = STATE_FINISHED;
			DebugMSG("finished");
		}
	}
}


void Server::handleEvents(unsigned int events) {
	if (events & (EPOLLHUP | EPOLLERR)) {
		if ( stateLocal >= STATE_OPEN
		  && stateLocal < STATE_CLOSING )
			close((bool)(events & EPOLLERR));
		return;
	}
	
	if (events & EPOLLIN) {
		if ( stateLocal >= STATE_OPEN
		  && stateLocal < STATE_CLOSE_REQ)
		{
			unsigned char address[MAX_ADDR_SIZE] = {};
			socklen_t len = sizeof(address);
			int res = ::accept(sockId, (struct sockaddr*)address, &len);
			assert(len < sizeof(address));
			if (res >= 0)
				if (Connection *conn = onConnect(address, len))
					if (!conn->open(*protocol, address, len, res, this))
						onDisconnect(conn, true);
		}
	}
}



Connection* Server::onConnect(const void*, size_t) { return nullptr; }
void Server::onDisconnect(Connection*, bool) { }