//
// TCPServer.cpp
//
// Library: Net
// Package: TCPServer
// Module:  TCPServer
//
// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier:	BSL-1.0
//


#include "Poco/Net/TCPServer.h"
#include "Poco/Net/TCPServerDispatcher.h"
#include "Poco/Net/TCPServerConnection.h"
#include "Poco/Net/TCPServerConnectionFactory.h"
#include "Poco/Timespan.h"
#include "Poco/Exception.h"
#include "Poco/ErrorHandler.h"


using Poco::ErrorHandler;


namespace Poco {
namespace Net {


//
// TCPServerConnectionFilter
//


TCPServerConnectionFilter::~TCPServerConnectionFilter()
{
}


//
// TCPServer
//


TCPServer::TCPServer(TCPServerConnectionFactory::Ptr pFactory, Poco::UInt16 portNumber, TCPServerParams::Ptr pParams):
	_socket(ServerSocket(portNumber)),
	_thread(threadName(_socket)),
	_stopped(true)
{	
	Poco::ThreadPool& pool = Poco::ThreadPool::defaultPool();
	if (pParams)
	{
		int toAdd = pParams->getMaxThreads() - pool.capacity();
		if (toAdd > 0) pool.addCapacity(toAdd);
	}
	_pDispatcher = new TCPServerDispatcher(pFactory, pool, pParams);
	
}


TCPServer::TCPServer(TCPServerConnectionFactory::Ptr pFactory, const ServerSocket& socket, TCPServerParams::Ptr pParams):
	_socket(socket),
	_thread(threadName(socket)),
	_stopped(true)
{
	Poco::ThreadPool& pool = Poco::ThreadPool::defaultPool();
	if (pParams)
	{
		int toAdd = pParams->getMaxThreads() - pool.capacity();
		if (toAdd > 0) pool.addCapacity(toAdd);
	}
	_pDispatcher = new TCPServerDispatcher(pFactory, pool, pParams);
}


TCPServer::TCPServer(TCPServerConnectionFactory::Ptr pFactory, Poco::ThreadPool& threadPool, const ServerSocket& socket, TCPServerParams::Ptr pParams):
	_socket(socket),
	_pDispatcher(new TCPServerDispatcher(pFactory, threadPool, pParams)),
	_thread(threadName(socket)),
	_stopped(true)
{
}


TCPServer::~TCPServer()
{
	try
	{
		stop();
		_pDispatcher->release();
	}
	catch (...)
	{
		poco_unexpected();
	}
}


const TCPServerParams& TCPServer::params() const
{
	return _pDispatcher->params();
}


void TCPServer::start()
{
	poco_assert (_stopped);

	_stopped = false;
	_thread.start(*this);
}

	
void TCPServer::stop()
{
	if (!_stopped)
	{
		_stopped = true;
		_thread.join();
		_pDispatcher->stop();
	}
}


void TCPServer::run()
{
	while (!_stopped)
	{
		Poco::Timespan timeout(250000);
		try
		{
			if (_socket.poll(timeout, Socket::SELECT_READ))
			{
				try
				{
					StreamSocket ss = _socket.acceptConnection();
					
					if (!_pConnectionFilter || _pConnectionFilter->accept(ss))
					{
						// enable nodelay per default: OSX really needs that
#if defined(POCO_OS_FAMILY_UNIX)
						if (ss.address().family() != AddressFamily::UNIX_LOCAL)
#endif
						{
							ss.setNoDelay(true);
						}
						_pDispatcher->enqueue(ss);
					}
				}
				catch (Poco::Exception& exc)
				{
					ErrorHandler::handle(exc);
				}
				catch (std::exception& exc)
				{
					ErrorHandler::handle(exc);
				}
				catch (...)
				{
					ErrorHandler::handle();
				}
			}
		}
		catch (Poco::Exception& exc)
		{
			ErrorHandler::handle(exc);
			// possibly a resource issue since poll() failed;
			// give some time to recover before trying again
			Poco::Thread::sleep(50); 
		}
	}
}


int TCPServer::currentThreads() const
{
	return _pDispatcher->currentThreads();
}


int TCPServer::maxThreads() const
{
	return _pDispatcher->maxThreads();
}

	
int TCPServer::totalConnections() const
{
	return _pDispatcher->totalConnections();
}


int TCPServer::currentConnections() const
{
	return _pDispatcher->currentConnections();
}


int TCPServer::maxConcurrentConnections() const
{
	return _pDispatcher->maxConcurrentConnections();
}

	
int TCPServer::queuedConnections() const
{
	return _pDispatcher->queuedConnections();
}


int TCPServer::refusedConnections() const
{
	return _pDispatcher->refusedConnections();
}


void TCPServer::setConnectionFilter(const TCPServerConnectionFilter::Ptr& pConnectionFilter)
{
	poco_assert (_stopped);

	_pConnectionFilter = pConnectionFilter;
}


std::string TCPServer::threadName(const ServerSocket& socket)
{
#if _WIN32_WCE == 0x0800
	// Workaround for WEC2013: only the first call to getsockname()
	// succeeds. To mitigate the impact of this bug, do not call
	// socket.address(), which calls getsockname(), here.
	std::string name("TCPServer");
	#pragma message("Using WEC2013 getsockname() workaround in TCPServer::threadName(). Remove when no longer needed.")
#else
	std::string name("TCPServer: ");
	name.append(socket.address().toString());
#endif
	return name;

}


} } // namespace Poco::Net
