

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  This file is part of CMSDK version 2.1.1.260726 (26 July 2026)
//_/_/  
//_/_/  developed by
//_/_/  
//_/_/  Communicative Machines Ltd. 
//_/_/    http://cmlabs.com/
//_/_/  Copyright (c) 2026
//_/_/  
//_/_/  in collaboration with 
//_/_/  
//_/_/  Icelandic Institute for Intelligent Machines ses
//_/_/    Menntavegur 1
//_/_/    101 Reykjavik
//_/_/    ICELAND
//_/_/    http://www.iiim.is/
//_/_/
//_/_/  and is released under the following
//_/_/  
//_/_/  EXTENDED BSD License
//_/_/
//_/_/  Redistribution and use in source and binary forms, with or without
//_/_/  modification, is permitted provided that the following conditions 
//_/_/  are met:
//_/_/
//_/_/  - Redistributions of source code must retain the above copyright 
//_/_/    and collaboration notice, this list of conditions and the following 
//_/_/    disclaimer.
//_/_/
//_/_/  - Redistributions in binary form must reproduce the above copyright 
//_/_/    notice, this list of conditions and the following disclaimer in the 
//_/_/    documentation and/or other materials provided with the distribution.
//_/_/
//_/_/  - Neither the name of its copyright holders nor the names of its 
//_/_/    contributors may be used to endorse or promote products derived from 
//_/_/    this software without specific prior written permission.
//_/_/
//_/_/  - CADIA Clause: The license granted in and to the software under this 
//_/_/    agreement is a limited-use license. The software may not be used in 
//_/_/    furtherance of: (i) intentionally causing bodily injury or severe 
//_/_/    emotional harm to any person; (ii) invading the personal privacy or 
//_/_/    violating the human rights of any person; or (iii) committing or 
//_/_/    preparing for any act of war. 
//_/_/
//_/_/  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
//_/_/  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
//_/_/  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
//_/_/  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 
//_/_/  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
//_/_/  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
//_/_/  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
//_/_/  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
//_/_/  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
//_/_/  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
//_/_/  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//_/_/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
/*! @file NetworkManager.h
	@brief Connection/channel management layer: multi-protocol listeners, typed dispatch, HTTP client — and the CMSDK's built-in HTTP/WebSocket server.

	NetworkManager is the top of the CMSDK networking stack. It owns
	NetworkChannel objects, each of which groups listeners and connections
	for one logical interface and dispatches parsed protocol objects
	(HTTPRequest, DataMessage, TelnetLine, WebsocketData) either by pushing
	them to a NetworkReceiver callback (async mode) or by queuing them for
	waitFor*() calls (sync mode).

	Key capabilities:
	- @b Full @b HTTP(S)/WebSocket @b server: createListener() with
	  ::PROTOCOL_HTTP_SERVER turns any port into a web server; the PsyProbe
	  diagnostics/monitoring web UI is served through exactly this mechanism.
	  There is deliberately no hard-coded default port — every listener port
	  is chosen by the caller (system spec); the RequestGateway layer adds
	  ports via addPort() the same way.
	- @b HTTP(S) @b client: the makeHTTPRequest() overloads perform blocking
	  request/response exchanges, including multipart POST bodies.
	- @b Protocol @b auto-detection: a listener can accept several protocols
	  on one port; the first bytes of each incoming connection are sniffed
	  (NetworkChannel::autoDetectConnection()) to pick the handler thread.
	- @b Reconnection: connections created with @c autoreconnect=true are
	  transparently re-established after failures; observers learn about
	  lifecycle changes through NETWORKEVENT_* events.

	@par Threads
	The manager runs one management thread (NetworkManagerRun); each listener
	and each connection gets its own protocol-specific thread
	(HTTPServerRun, MessageConnectionRun, ...). Callback receivers are
	therefore invoked concurrently and must be thread-safe.

	@par Platform notes
	Built on NetworkConnections.h; all Winsock-vs-BSD-socket differences are
	encapsulated below this layer (see @ref NetworkConnections.h).

	A synchronous binary-message peer (listen, receive, reply) in a few lines:
	@code
	NetworkManager net;
	NetworkChannel* ch = net.createListener(9000, NOENC, PROTOCOL_MESSAGE,
	                                        false, 3000, true, 1, NULL); // sync mode
	uint64 conid;
	DataMessage* msg = ch->waitForMessage(conid, 1000);  // caller owns msg
	if (msg) {
	    DataMessage* reply = new DataMessage(msg);
	    reply->setString("Result", "ok");
	    ch->sendMessage(reply, conid);   // sendMessage does NOT take ownership
	    delete reply;
	    delete msg;
	}
	@endcode

	@ingroup Networking
*/
#if !defined(_NETWORKMANAGER_H_)
#define _NETWORKMANAGER_H_

#pragma once

#include "NetworkConnections.h"
#include "NetworkProtocols.h"


namespace	cmlabs{

/*! Notification of a connection lifecycle change (connect, disconnect, buffer state...).
	Delivered to NetworkReceiver::receiveNetworkEvent() or via
	NetworkChannel::waitForNetworkEvent(). See the NETWORKEVENT_* constants.
	@ingroup Networking */
struct NetworkEvent {
	uint64 cid;			/*!< Channel id the event belongs to. */
	uint64 conid;		/*!< Connection id within the channel. */
	uint64 time;		/*!< Event timestamp (ms epoch). */
	uint8 type;			/*!< NETWORKEVENT_* event type. */
	uint8 protocol;		/*!< PROTOCOL_* of the affected connection. */
};

/////////////////////////////////////////////////////////////
// Receiver Interface
/////////////////////////////////////////////////////////////

class NetworkChannel;
/*! Callback interface for asynchronous delivery of parsed network traffic.

	Implement the receive*() methods you care about and pass the object to
	NetworkManager::createListener() / create*Connection(); each incoming
	item is then pushed to you instead of being queued for waitFor*().
	Default implementations return false, meaning "not handled" — unhandled
	items are queued on the channel instead.
	@note Callbacks arrive on the connection's protocol thread; if the
	receiver takes ownership of the passed object it must delete it.
	@ingroup Networking */
class NetworkReceiver {
public:
	NetworkReceiver() {}
	/*! A connection lifecycle event occurred. @param evt Event data. @param channel Originating channel. @param conid Connection id. @return true if handled (and ownership taken). */
	virtual bool receiveNetworkEvent(NetworkEvent* evt, NetworkChannel* channel, uint64 conid) {return false;}
	/*! An HTTP request arrived on a server connection. @param req The request. @param channel Originating channel. @param conid Connection id (use for sendHTTPReply()). @return true if handled. */
	virtual bool receiveHTTPRequest(HTTPRequest* req, NetworkChannel* channel, uint64 conid) {return false;}
	/*! A binary DataMessage arrived. @param msg The message. @param channel Originating channel. @param conid Connection id. @return true if handled. */
	virtual bool receiveMessage(DataMessage* msg, NetworkChannel* channel, uint64 conid) {return false;}
	/*! A Telnet line arrived. @param line The line. @param channel Originating channel. @param conid Connection id. @return true if handled. */
	virtual bool receiveTelnetLine(TelnetLine* line, NetworkChannel* channel, uint64 conid) {return false;}
	/*! An HTTP reply to an earlier async request arrived. @param reply The reply. @param req The originating request. @param channel Channel. @param conid Connection id. @return true if handled. */
	virtual bool receiveHTTPReply(HTTPReply* reply, HTTPRequest* req, NetworkChannel* channel, uint64 conid) {return false;}
	/*! A WebSocket message arrived. @param wsData The frame/message. @param channel Channel. @param conid Connection id. @return true if handled. */
	virtual bool receiveWebsocketData(WebsocketData* wsData, NetworkChannel* channel, uint64 conid) { return false; }
};

/*! Minimal HTTP server used by the unit tests: replies with a canned page.
	@ingroup Networking */
class HTTPTestServer : public NetworkReceiver {
public:
	HTTPTestServer();
	~HTTPTestServer();
	/*! Serve a test page for any request. @return true always. */
	virtual bool receiveHTTPRequest(HTTPRequest* req, NetworkChannel* channel, uint64 conid);
};

/*! Minimal WebSocket echo server used by the unit tests (handles upgrade + echo).
	@ingroup Networking */
class WebsocketTestServer : public NetworkReceiver {
public:
	WebsocketTestServer();
	~WebsocketTestServer();
	/*! Answer the WebSocket upgrade handshake. @return true when upgraded. */
	virtual bool receiveHTTPRequest(HTTPRequest* req, NetworkChannel* channel, uint64 conid);
	/*! Echo received frames back to the client. @return true when handled. */
	virtual bool receiveWebsocketData(WebsocketData* wsData, NetworkChannel* channel, uint64 conid);
};

/////////////////////////////////////////////////////////////
// Network Manager
/////////////////////////////////////////////////////////////

class NetworkChannel;
/*! Central owner of all channels, listeners and connections in a process.

	Create one NetworkManager, then use the create*() factories to open
	listeners and outgoing connections. Each factory associates the new
	listener/connection with a NetworkChannel (identified by @c channelID;
	a new channel is created when needed) and an optional NetworkReceiver
	for push-mode dispatch. The manager's own thread supervises delayed
	connects and reconnection.

	It is also a complete embedded HTTP(S)/WebSocket server — e.g. the
	PsyProbe web monitoring UI is served by a NetworkManager HTTP listener.
	Ports are always caller-chosen; there is no built-in default port.

	@par Ownership
	The manager owns its channels and connections. Returned NetworkChannel
	pointers remain valid until the corresponding stop/end/remove call or
	manager destruction. HTTPReply objects returned by makeHTTPRequest()
	are owned by the caller.

	@par Thread-safety
	Public methods may be called from any thread; internal maps are guarded.
	makeHTTPRequest() is blocking (up to its timeout); the create*Connection()
	factories block up to @c timeoutMS unless a delayed/async variant is used.
	@ingroup Networking */
class NetworkManager : public Runnable {
	friend class NetworkChannel;
public:
	/*! Basic TCP/message-protocol round-trip self-test. @return true on success. */
	static bool UnitTest();
	/*! Self-test of non-blocking (delayed) connect handling. @return true on success. */
	static bool UnitTestDelayedConnect();
	/*! Self-test of the built-in HTTP server and client. @return true on success. */
	static bool UnitTestHTTP();
#ifdef _USE_SSL_
	/*! HTTPS round-trip self-test with a runtime-generated self-signed certificate. @return true on success. */
	static bool UnitTestHTTPS();
	/*! Self-test of SSL peer verification policy handling. @return true on success. */
	static bool UnitTestSSLVerify();
	/*! Self-test of custom CA file/path verification. @return true on success. */
	static bool UnitTestSSLHostCA();
#endif // _USE_SSL_
	/*! Fetch a list of URLs from a host and report results (manual test helper).
		@param host Server host. @param port Server port. @param urls URI paths to fetch.
		@return true when all fetches succeeded. */
	static bool TestHTTP(const char* host, uint32 port, std::vector<std::string> &urls);
	/*! WebSocket upgrade + echo self-test. @return true on success. */
	static bool WebsocketTest();

	/*! Thread entry point of the manager's supervision loop (do not call directly). */
	THREAD_RET THREAD_FUNCTION_CALL NetworkManagerRun(THREAD_ARG arg);

	NetworkManager();
	virtual ~NetworkManager();

	/*! Set the certificate/key used by SSL listeners created via this manager.
		@param sslCertPath PEM certificate path. @param sslKeyPath PEM private key path.
		@return true if stored. Must be set before creating ::SSLENC listeners. */
	bool setSSLCertificate(const char* sslCertPath, const char* sslKeyPath);

	// SSL client verification policy for connections made through this manager:
	// -1 = use the process-wide default (SSLConnection::GetDefaultAllowSelfSigned),
	// 0 = verify peer certificates (secure), 1 = allow self-signed/untrusted peers.
	bool setSSLAllowSelfSigned(bool allow) { sslAllowSelfSigned = allow ? 1 : 0; return true; }
	int8 sslAllowSelfSigned;
	// Custom CA file/dir for SSL client peer verification on connections made
	// through this manager (empty = inherit process default / OS trust store)
	bool setSSLCALocation(const char* caFile, const char* caPath) { sslCAFile = caFile ? caFile : ""; sslCAPath = caPath ? caPath : ""; sslCASet = true; return true; }
	std::string sslCAFile;
	std::string sslCAPath;
	bool sslCASet;

	/*! Open a listening port for one or more protocols.
		@param port TCP port to bind (caller-chosen; no default).
		@param encryption ::NOENC or ::SSLENC (certificate must be set first).
		@param protocol PROTOCOL_* bit flags; several may be OR'ed for auto-detection.
		@param isAsync Push items to @p recv when true; queue for waitFor*() when false.
		@param protocolTimeout Milliseconds allowed to sniff/auto-detect a new connection's protocol.
		@param isDefaultProtocol Treat the first protocol flag as fallback when detection is inconclusive.
		@param channelID Channel to attach to (0 = allocate a new channel).
		@param recv Receiver for async dispatch (may be NULL for sync mode).
		@return The channel serving the listener, or NULL if the port could not be bound. Owned by the manager. */
	NetworkChannel* createListener(uint16 port, uint8 encryption, uint8 protocol, bool isAsync, uint32 protocolTimeout, bool isDefaultProtocol, uint32 channelID, NetworkReceiver* recv);
	/*! Connect to a remote host by name/IP and start the protocol thread.
		@param addr Host name or IPv4 string. @param port Remote port.
		@param encryption ::NOENC or ::SSLENC. @param protocol Single PROTOCOL_* flag.
		@param isAsync Push mode when true. @param autoreconnect Re-establish automatically on failure.
		@param channelID Channel to attach to (0 = new). @param recv Receiver for async dispatch.
		@param conid Out: new connection id. @param location Out: resolved endpoint (packed uint64).
		@param timeoutMS Connect timeout in ms.
		@return The channel, or NULL on failure. Blocking up to @p timeoutMS. */
	NetworkChannel* createTCPConnection(const char* addr, uint16 port, uint8 encryption, uint8 protocol, bool isAsync, bool autoreconnect, uint32 channelID, NetworkReceiver* recv, uint64& conid, uint64& location, uint32 timeoutMS = 5000);
	/*! Connect to a packed uint64 endpoint. See the string-address overload for details.
		@param location Packed endpoint (address+port).
		@param encryption ::NOENC or ::SSLENC. @param protocol Single PROTOCOL_* flag.
		@param isAsync Push mode when true. @param channelID Channel to attach to (0 = new).
		@param recv Receiver for async dispatch. @param conid Out: new connection id.
		@param timeoutMS Connect timeout in ms.
		@param autoreconnect Re-establish automatically on failure. */
	NetworkChannel* createTCPConnection(uint64 location, uint8 encryption, uint8 protocol, bool isAsync, bool autoreconnect, uint32 channelID, NetworkReceiver* recv, uint64& conid, uint32 timeoutMS = 5000);
	/*! Connect trying several candidate IPv4 addresses for one host.
		@param addresses Candidate addresses. @param addressCount Number of candidates.
		@param port TCP port. @param location Out: resolved endpoint (packed uint64).
		@param encryption ::NOENC or ::SSLENC. @param protocol Single PROTOCOL_* flag.
		@param isAsync Push mode when true. @param channelID Channel to attach to (0 = new).
		@param recv Receiver for async dispatch. @param conid Out: new connection id.
		@param timeoutMS Connect timeout in ms.
		@param autoreconnect Re-establish automatically on failure. */
	NetworkChannel* createTCPConnection(const uint32* addresses, uint16 addressCount, uint16 port, uint8 encryption, uint8 protocol, bool isAsync, bool autoreconnect, uint32 channelID, NetworkReceiver* recv, uint64& conid, uint64& location, uint32 timeoutMS = 5000);
	/*! Bind a UDP port for datagram traffic.
		@param port Local UDP port. @param protocol PROTOCOL_* flag (typically PROTOCOL_MESSAGE).
		@param isAsync Push mode when true. @param autoreconnect Rebind on failure.
		@param channelID Channel to attach to (0 = new). @param recv Receiver.
		@param conid Out: new connection id. @return The channel, or NULL on failure. */
	NetworkChannel* createUDPConnection(uint16 port, uint8 protocol, bool isAsync, bool autoreconnect, uint32 channelID, NetworkReceiver* recv, uint64& conid);
	/*! Like createTCPConnection() but sends optional greeting bytes right after
		connecting (peer identification / protocol selection) and never auto-reconnects.
		@param addr Host address. @param port TCP port. @param location Out: resolved endpoint (packed uint64).
		@param encryption ::NOENC or ::SSLENC. @param protocol Single PROTOCOL_* flag.
		@param isAsync Push mode when true. @param channelID Channel to attach to (0 = new).
		@param recv Receiver for async dispatch. @param conid Out: new connection id.
		@param timeoutMS Connect timeout in ms.
		@param greetingData Bytes sent immediately after connect (copied). @param greetingSize Byte count. */
	NetworkChannel* addTCPConnection(const char* addr, uint16 port, uint8 encryption, uint8 protocol, bool isAsync, uint32 channelID, NetworkReceiver* recv, uint64& conid, uint64& location, uint32 timeoutMS = 5000, const char* greetingData = NULL, uint32 greetingSize = 0);
	/*! Packed-endpoint variant of addTCPConnection().
		@param location Packed endpoint.
		@param encryption ::NOENC or ::SSLENC. @param protocol Single PROTOCOL_* flag.
		@param isAsync Push mode when true. @param channelID Channel to attach to (0 = new).
		@param recv Receiver for async dispatch. @param conid Out: new connection id.
		@param timeoutMS Connect timeout in ms.
		@param greetingData Bytes sent immediately after connect (copied). @param greetingSize Byte count. */
	NetworkChannel* addTCPConnection(uint64 location, uint8 encryption, uint8 protocol, bool isAsync, uint32 channelID, NetworkReceiver* recv, uint64& conid, uint32 timeoutMS = 5000, const char* greetingData = NULL, uint32 greetingSize = 0);
	/*! Open a client WebSocket connection from a full URL (ws:// or wss://).
		@param url Full URL including host, port and path.
		@param channelID Channel to attach to (0 = new). @param recv Receiver for frames.
		@param conid Out: new connection id.
		@param protocolName Optional Sec-WebSocket-Protocol. @param origin Optional Origin header.
		@param timeoutMS Connect+handshake timeout in ms.
		@return The channel, or NULL on failure. Blocking up to @p timeoutMS. */
	NetworkChannel* createWebsocketConnection(const char* url, uint32 channelID, NetworkReceiver* recv, uint64& conid, const char* protocolName = NULL, const char* origin = NULL, uint32 timeoutMS = 5000);
	/*! Open a client WebSocket connection from explicit components.
		@param uri Request path. @param addr Host. @param port Port. @param encryption ::NOENC or ::SSLENC.
		@param channelID Channel to attach to (0 = new). @param recv Receiver for frames.
		@param conid Out: new connection id.
		@param protocolName Optional Sec-WebSocket-Protocol. @param origin Optional Origin header.
		@param timeoutMS Connect+handshake timeout in ms. */
	NetworkChannel* createWebsocketConnection(const char* uri, const char* addr, uint16 port, uint8 encryption, uint32 channelID, NetworkReceiver* recv, uint64& conid, const char* protocolName = NULL, const char* origin = NULL, uint32 timeoutMS = 5000);


	/*! Stop a listener previously opened with createListener().
		@param port Listener port. @param protocol PROTOCOL_* flags it was created with.
		@return true if found and stopped. */
	bool stopListener(uint16 port, uint8 protocol);
	/*! Gracefully close a connection (thread is asked to finish; entry kept for reuse).
		@param conid Connection id. @return true if found. */
	bool endConnection(uint64 conid);
	/*! Close the UDP connection bound to @p port. @return true if found. */
	bool endUDPConnection(uint16 port);
	/*! Close a connection and remove it from the manager's maps entirely.
		@param conid Connection id. @return true if found and removed. */
	bool removeConnection(uint64 conid);

	/*! @param conid Connection id. @return Transport type (::TCPCON etc.), or 0 if unknown. */
	uint8 getConnectionType(uint64 conid);
	/*! @param conid Connection id. @return Remote endpoint packed as uint64, or 0. */
	uint64 getRemoteAddress(uint64 conid);
	/*! @param conid Connection id. @return The owning channel, or NULL. Owned by the manager. */
	NetworkChannel* getConnection(uint64 conid);
	/*! @param port Listener port. @return The channel listening on that TCP port, or NULL. */
	NetworkChannel* getTCPConnectionByPort(uint16 port);
	/*! @param port UDP port. @return The channel bound to that UDP port, or NULL. */
	NetworkChannel* getUDPConnectionByPort(uint16 port);
	/*! Register an externally created channel with the manager.
		@param channel Channel to adopt (ownership transfers). @return Its new connection id. */
	uint64 addConnection(NetworkChannel* channel);

	/*! Send a DataMessage as a UDP datagram via the shared output socket.
		@param msg Message (caller keeps ownership). @param destination Packed uint64 endpoint.
		@return true if the datagram was sent (delivery not guaranteed). */
	bool sendUDPMessage(DataMessage* msg, uint64 destination);

	/*! Blocking HTTP(S) exchange from a URL string (GET, or POST when @p content given).
		@param url Full URL (http:// or https://, may embed basic-auth credentials).
		@param timeout Total timeout in ms. @param content Optional request body (POST).
		@param contentSize Body size. @return New reply owned by the caller, or NULL on failure/timeout. */
	HTTPReply* makeHTTPRequest(const char* url, uint32 timeout, const char* content = NULL, uint32 contentSize = 0);
	/*! Blocking exchange sending a pre-built request to an explicit endpoint.
		@param req Request to send (caller keeps ownership). @param addr Host. @param port Port.
		@param encryption ::NOENC or ::SSLENC. @param timeout Total timeout in ms.
		@return New reply owned by the caller, or NULL. */
	HTTPReply* makeHTTPRequest(HTTPRequest* req, const char* addr, uint16 port, uint8 encryption, uint32 timeout);

	/*! Blocking exchange with custom headers and typed raw body.
		@param ops HTTP_* method id. @param addr Full URL or host address. @param timeout ms.
		@param headerEntries Extra header name/value pairs. @param content Body bytes.
		@param contentType Body MIME type. @param contentSize Body size.
		@param keepAlive Request a persistent connection. @param ifModifiedSince ms epoch or 0.
		@return New reply owned by the caller, or NULL. */
	HTTPReply* makeHTTPRequest(uint8 ops, std::string addr, uint32 timeout,
		std::map<std::string, std::string>& headerEntries, const char* content, const char* contentType, uint32 contentSize,
		bool keepAlive = false, uint64 ifModifiedSince = 0);

	/*! Blocking multipart/form-data exchange (file uploads, form posts).
		@param ops HTTP_* method id. @param addr Full URL or host address. @param timeout ms.
		@param headerEntries Extra headers. @param bodyEntries Named body parts (not owned).
		@param keepAlive Persistent connection flag. @param ifModifiedSince ms epoch or 0.
		@return New reply owned by the caller, or NULL. */
	HTTPReply* makeHTTPRequest(uint8 ops, std::string addr, uint32 timeout,
		std::map<std::string,std::string>& headerEntries, std::map<std::string, HTTPPostEntry*>& bodyEntries,
		bool keepAlive = false, uint64 ifModifiedSince = 0);

protected:
	std::map<uint32, NetworkChannel*> channels;				/*!< All channels by channel id (owned). */
	std::map<uint64, NetworkChannel*> channelsByConnection;	/*!< Channel lookup by connection id. */
	std::map<uint16, NetworkChannel*> listeners;			/*!< TCP listeners by port. */
	std::map<uint16, NetworkChannel*> udpListeners;			/*!< UDP listeners by port. */

	std::string sslCertPath;	/*!< PEM certificate path for SSL listeners. */
	std::string sslKeyPath;		/*!< PEM private key path for SSL listeners. */

	UDPConnection* udpOutputCon;		/*!< Shared output-only UDP socket for sendUDPMessage(). */
	utils::Mutex udpOutputConMutex;		/*!< Serialises use of ::udpOutputCon. */

	uint32 lastChannelID;		/*!< Last channel id issued. */
	uint64 lastConnectionID;	/*!< Last connection id issued. */
	/*! Supervision loop body (delayed connects, reconnection). @return true to keep running. */
	bool run();
};

/////////////////////////////////////////////////////////////
// Connections
/////////////////////////////////////////////////////////////

/*! @name Network event types
	Values of NetworkEvent::type describing connection lifecycle changes. @{ */
#define NETWORKEVENT_CONNECT				1	/*!< Connection established. */
#define NETWORKEVENT_RECONNECT				2	/*!< Connection re-established after a failure (autoreconnect). */
#define NETWORKEVENT_DISCONNECT				3	/*!< Connection closed for good. */
#define NETWORKEVENT_DISCONNECT_RETRYING	4	/*!< Connection lost; reconnection attempts in progress. */
#define NETWORKEVENT_BUFFER_LOW				5	/*!< Receive buffer back below the pressure threshold. */
#define NETWORKEVENT_BUFFER_FULL			6	/*!< Receive buffer full; incoming data may back up. */
#define NETWORKEVENT_UNPROCESSED_DATA		7	/*!< Bytes arrived that no protocol handler consumed. */
#define NETWORKEVENT_PROTOCOL_ERROR			8	/*!< Protocol parsing/framing error on the connection. */
/*! @} */

/*! Bookkeeping for one worker thread of a NetworkChannel (per listener or connection).

	Plain data holder tying together the thread, its socket object (listener
	or connection), protocol settings and lifecycle flags. Created and owned
	by NetworkChannel; not for direct external use.
	@ingroup Networking */
class NetworkThread {
public:
	/*! @param parent Owning channel. @param id Connection/listener id this thread serves. */
	NetworkThread(NetworkChannel* parent, uint64 id);
	~NetworkThread();

	uint64 id;					/*!< Connection or listener id served by this thread. */
	uint32 threadID;			/*!< ThreadManager id of the worker thread. */
	uint16 port;				/*!< Local port (listeners) or 0. */
	TCPListener* listener;		/*!< Listener served (owned), or NULL for connections. */
	NetworkConnection* con;		/*!< Connection served (owned), or NULL for listeners. */
	uint32 defaultProtocol;		/*!< Fallback PROTOCOL_* when auto-detection is inconclusive. */
	uint32 autoProtocols;		/*!< PROTOCOL_* bit set to auto-detect among. */
	uint32 autoProtocolTimeout;	/*!< Milliseconds allowed for protocol sniffing. */
	bool autoreconnect;			/*!< Re-establish the connection automatically on failure. */
	bool shouldContinue;		/*!< Loop control flag: thread exits when false. */
	bool isRunning;				/*!< True while the worker loop is active. */
	bool isAsync;				/*!< Push to receiver (true) or queue for waitFor*() (false). */
	HTTPRequest* lastRequest;	/*!< Last request pending a reply on this HTTP connection. */
	NetworkChannel* parent;		/*!< Owning channel (not owned). */
};

/*! One logical network interface: a group of listeners/connections with shared dispatch.

	A channel bundles related listeners and connections (e.g. "the web
	interface on port 8080" or "the gateway link"), gives each its own
	worker thread, and funnels all parsed traffic either to the channel's
	NetworkReceiver (async) or into typed wait queues consumed via the
	waitFor*() methods (sync). Send methods are addressed by connection id.

	Channels are created and owned by NetworkManager; user code normally
	obtains one from the manager's factory methods rather than constructing
	it directly.
	@par Thread-safety
	All public methods are safe to call from any thread; the waitFor*()
	methods block up to their timeout and return ownership of the object.
	@ingroup Networking */
class NetworkChannel {
	friend class NetworkManager;
public:
	/*! @param manager Owning manager (not owned by the channel). */
	NetworkChannel(NetworkManager* manager);
	virtual ~NetworkChannel();
	uint32 cid;	/*!< Channel id within the manager. */

	/*! @param conid Connection id. @return true if that connection is currently up. */
	bool isConnected(uint64 conid);
	// getStats();

	/*! Stop all worker threads and close all listeners/connections of this channel.
		@return true when shut down. */
	bool shutdown();

	/*! Open a listener on this channel (see NetworkManager::createListener() for semantics).
		@param cid Channel id to tag events with. @param port TCP port. @param encryption ::NOENC or ::SSLENC.
		@param protocol PROTOCOL_* flags. @param isAsync Push vs queue dispatch.
		@param protocolTimeout Sniffing timeout in ms. @param isDefaultProtocol First flag is the fallback.
		@return true if the port was bound. */
	bool startListener(uint64 cid, uint16 port, uint8 encryption, uint8 protocol, bool isAsync, uint32 protocolTimeout = 3000, bool isDefaultProtocol = false);
	/*! Stop a listener on this channel. @param port Port. @param protocol Its PROTOCOL_* flags. @return true if found. */
	bool stopListener(uint16 port, uint8 protocol);

	/*! Connect to a host by name/IP; see NetworkManager::createTCPConnection().
		@return The new connection id, or 0 on failure. Blocking up to @p timeoutMS. */
	uint64 createTCPConnection(const char* addr, uint16 port, uint8 encryption, uint8 protocol, bool isAsync, bool autoreconnect, uint64& location, uint32 timeoutMS = 5000);
	/*! Connect to a packed uint64 endpoint. @return The new connection id, or 0. */
	uint64 createTCPConnection(uint64 location, uint8 encryption, uint8 protocol, bool isAsync, bool autoreconnect, uint32 timeoutMS = 5000);
	/*! Connect trying several candidate addresses. @return The new connection id, or 0. */
	uint64 createTCPConnection(const uint32* addresses, uint16 addressCount, uint16 port, uint8 encryption, uint8 protocol, bool isAsync, bool autoreconnect, uint64& location, uint32 timeoutMS = 5000);
	/*! Bind a UDP port on this channel. @return The new connection id, or 0. */
	uint64 createUDPConnection(uint16 port, uint8 protocol, bool isAsync, bool autoreconnect);
	/*! Connect with optional greeting bytes (no autoreconnect); see NetworkManager::addTCPConnection().
		@return The new connection id, or 0. */
	uint64 addTCPConnection(const char* addr, uint16 port, uint8 encryption, uint8 protocol, bool isAsync, uint64& location, uint32 timeoutMS = 5000, const char* greetingData = NULL, uint32 greetingSize = 0);
	/*! Packed-endpoint variant of addTCPConnection(). @return The new connection id, or 0. */
	uint64 addTCPConnection(uint64 location, uint8 encryption, uint8 protocol, bool isAsync, uint32 timeoutMS = 5000, const char* greetingData = NULL, uint32 greetingSize = 0);
	
	/*! Open a client WebSocket connection from a full URL. @return The new connection id, or 0. */
	uint64 createWebsocketConnection(const char* url, const char* protocolName, const char* origin = NULL, uint32 timeoutMS = 5000);
	/*! Open a client WebSocket connection from explicit components. @return The new connection id, or 0. */
	uint64 createWebsocketConnection(const char* uri, const char* addr, uint16 port, uint8 encryption, const char* protocolName, const char* origin = NULL, uint32 timeoutMS = 5000);

	/*! Gracefully close a connection. @param conid Connection id. @return true if found. */
	bool endConnection(uint64 conid);
	/*! Close the UDP connection bound to @p port. @return true if found. */
	bool endUDPConnection(uint16 port);
	/*! @param conid Connection id. @return Transport type (::TCPCON etc.), or 0. */
	uint8 getConnectionType(uint64 conid);
	/*! @param conid Connection id. @return Remote endpoint packed as uint64, or 0. */
	uint64 getRemoteAddress(uint64 conid);

	/*! Adopt an incoming connection whose protocol is not yet known: sniff its
		first bytes against @p autoProtocols and start the matching handler thread.
		@param con The fresh connection (ownership transfers).
		@param port Listener port it arrived on.
		@param autoProtocols PROTOCOL_* bit set to test.
		@param autoProtocolTimeout Max sniffing time in ms.
		@param defaultProtocol Fallback protocol when detection is inconclusive (0 = drop).
		@param isAsync Push vs queue dispatch. @param autoreconnect Reconnect on failure.
		@return The new connection id, or 0 on failure. */
	uint64 autoDetectConnection(NetworkConnection* con, uint16 port, uint32 autoProtocols, uint32 autoProtocolTimeout, uint32 defaultProtocol, bool isAsync, bool autoreconnect);

	/*! Start the protocol worker thread for an already-connected connection.
		@param con The connection (ownership transfers). @param protocol Single PROTOCOL_* flag.
		@param isAsync Push vs queue dispatch. @param autoreconnect Reconnect on failure.
		@param timeoutMS Timeout used during any initial exchange.
		@return The new connection id, or 0 on failure. */
	uint64 startConnection(NetworkConnection* con, uint8 protocol, bool isAsync, bool autoreconnect, uint32 timeoutMS = 5000);

	// Per-channel SSL client verification override (-1 = inherit from manager /
	// process default). E.g. set per <interface allowselfsigned=".."> in the spec.
	bool setSSLAllowSelfSigned(bool allow) { sslAllowSelfSigned = allow ? 1 : 0; return true; }
	int8 sslAllowSelfSigned;
	// Per-channel custom CA file/dir override for SSL client peer verification
	// (e.g. set per <interface cafile=".." capath=".."> in the spec)
	bool setSSLCALocation(const char* caFile, const char* caPath) { sslCAFile = caFile ? caFile : ""; sslCAPath = caPath ? caPath : ""; sslCASet = true; return true; }
	std::string sslCAFile;
	std::string sslCAPath;
	bool sslCASet;
	// Apply the effective allow-self-signed policy to a new SSL client connection
	void applySSLClientPolicy(SSLConnection* con);

	/*! Replace the channel's receiver for async dispatch.
		@param recv New receiver (not owned; must outlive the channel or be replaced).
		@return true always. */
	bool setNewReceiver(NetworkReceiver* recv);

	/*! Wait for the next connection lifecycle event (sync mode).
		@param ms Max wait in milliseconds.
		@return New event owned by the caller, or NULL on timeout. Blocking. */
	NetworkEvent* waitForNetworkEvent(uint32 ms);

	/*! Wait for the next queued HTTP request (sync-mode HTTP server).
		@param conid Out: connection the request arrived on (use with sendHTTPReply()).
		@param ms Max wait in ms. @return New request owned by the caller, or NULL. Blocking. */
	HTTPRequest* waitForHTTPRequest(uint64& conid, uint32 ms);
	/*! Wait for the next queued Telnet line.
		@param conid Out: source connection. @param ms Max wait in ms.
		@return New line owned by the caller, or NULL. Blocking. */
	TelnetLine* waitForTelnetLine(uint64& conid, uint32 ms);
	/*! Wait for the next queued DataMessage.
		@param conid Out: source connection. @param ms Max wait in ms.
		@return New message owned by the caller, or NULL. Blocking. */
	DataMessage* waitForMessage(uint64& conid, uint32 ms);
	/*! Wait for the next queued HTTP reply (sync-mode HTTP client).
		@param conid Out: source connection. @param ms Max wait in ms.
		@return New reply owned by the caller, or NULL. Blocking. */
	HTTPReply* waitForHTTPReply(uint64& conid, uint32 ms);
	/*! Wait for the next queued WebSocket message.
		@param conid Out: source connection. @param ms Max wait in ms.
		@return New frame owned by the caller, or NULL. Blocking. */
	WebsocketData* waitForWebsocketData(uint64& conid, uint32 ms);

	/*! Send an HTTP reply on a server connection.
		@param reply Reply (caller keeps ownership). @param conid Destination connection.
		@return true if fully sent. */
	bool sendHTTPReply(HTTPReply* reply, uint64 conid);
	/*! Send a Telnet line. @param line Line (caller keeps ownership). @param conid Destination. @return true if sent. */
	bool sendTelnetLine(TelnetLine* line, uint64 conid);
	/*! Send a DataMessage. @param msg Message (caller keeps ownership). @param conid Destination. @return true if sent. */
	bool sendMessage(DataMessage* msg, uint64 conid);
	/*! Send an HTTP request without waiting for the reply (reply arrives via
		receiveHTTPReply()/waitForHTTPReply()). @param req Request (caller keeps ownership).
		@param conid Destination. @return true if sent. */
	bool sendHTTPRequest(HTTPRequest* req, uint64 conid);
	/*! Send an HTTP request and block for its reply on the same connection.
		@param req Request (caller keeps ownership). @param conid Destination.
		@param timeout Max wait in ms. @return New reply owned by the caller, or NULL. Blocking. */
	HTTPReply* sendReceiveHTTPRequest(HTTPRequest* req, uint64 conid, uint32 timeout);
	/*! Send a Telnet line and block for the response line.
		@param line Line to send (caller keeps ownership). @param conid Destination.
		@param timeout Max wait in ms. @param size Exact response byte count (0 = up to CR/LF).
		@return New line owned by the caller, or NULL. Blocking. */
	TelnetLine* sendReceiveTelnetLine(TelnetLine* line, uint64 conid, uint32 timeout, uint32 size = 0);
	/*! Send a WebSocket frame. @param wsData Frame (caller keeps ownership). @param conid Destination. @return true if sent. */
	bool sendWebsocketData(WebsocketData* wsData, uint64 conid);

	/*! @param conid Connection id. @return Recent outbound throughput in bytes/second. */
	uint32 getOutputSpeed(uint64 conid);
	/*! @param conid Connection id. @return Recent inbound throughput in bytes/second. */
	uint32 getInputSpeed(uint64 conid);

protected:
	NetworkManager* manager;		/*!< Owning manager (not owned). */
	NetworkReceiver* receiver;		/*!< Async dispatch target (not owned; may be NULL). */
	std::map<uint64, NetworkThread*> connectionThreads;	/*!< Worker threads by connection id (owned). */
	std::map<uint16, NetworkThread*> listeners;			/*!< TCP listener threads by port (owned). */
	std::map<uint16, NetworkThread*> udpListeners;		/*!< UDP listener threads by port (owned). */

	std::queue<NetworkEvent*> eventQueue;
	utils::Semaphore eventQueueSemaphore;
	utils::Mutex eventQueueMutex;

	/*! Queue or push a lifecycle event. @return true if delivered/queued. */
	bool enterNetworkEvent(uint8 type, uint8 protocol, uint64 conid);

	/*! Queue or push an incoming HTTP request. @return true if delivered/queued. */
	bool enterHTTPRequest(HTTPRequest* req, uint64 conid);
	/*! Queue or push an incoming HTTP reply. @return true if delivered/queued. */
	bool enterHTTPReply(HTTPReply* reply, HTTPRequest* req, uint64 conid);
	/*! Queue or push an incoming DataMessage. @return true if delivered/queued. */
	bool enterMessage(DataMessage* msg, uint64 conid);
	/*! Queue or push an incoming Telnet line. @return true if delivered/queued. */
	bool enterTelnetLine(TelnetLine* line, uint64 conid);
	/*! Queue or push an incoming WebSocket message. @return true if delivered/queued. */
	bool enterWebsocketData(WebsocketData* wsData, uint64 conid);

	std::queue<HTTPRequest*> queueHTTPRequests;
	utils::Semaphore queueHTTPRequestsSemaphore;
	utils::Mutex queueHTTPRequestsMutex;
	std::queue<HTTPReply*> queueHTTPReplies;
	utils::Semaphore queueHTTPRepliesSemaphore;
	utils::Mutex queueHTTPRepliesMutex;
	std::queue<DataMessage*> queueMessages;
	std::queue<uint64> queueMessageConIDs;   // source connection id per queued message (parallel to queueMessages)
	utils::Semaphore queueMessagesSemaphore;
	utils::Mutex queueMessagesMutex;
	std::queue<TelnetLine*> queueTelnetLines;
	utils::Semaphore queueTelnetLinesSemaphore;
	utils::Mutex queueTelnetLinesMutex;
	std::queue<WebsocketData*> queueWebsocketData;
	utils::Semaphore queueWebsocketDataSemaphore;
	utils::Mutex queueWebsocketDataMutex;

	utils::Mutex channelMutex;

	/*! Thread entry: accept loop for a TCP listener. */
	static THREAD_RET THREAD_FUNCTION_CALL NetworkListenerRun(THREAD_ARG arg);
	/*! Thread entry: protocol sniffing for a fresh connection (see autoDetectConnection()). */
	static THREAD_RET THREAD_FUNCTION_CALL ConnectionAutodetectRun(THREAD_ARG arg);

	/*! Thread entry: HTTP client connection loop (send requests, parse replies). */
	static THREAD_RET THREAD_FUNCTION_CALL HTTPClientRun(THREAD_ARG arg);
	/*! Thread entry: HTTP/WebSocket server connection loop — the built-in web
		server's per-connection worker (parses requests, dispatches, sends replies,
		handles the WebSocket upgrade and subsequent frames). */
	static THREAD_RET THREAD_FUNCTION_CALL HTTPServerRun(THREAD_ARG arg);
	/*! Thread entry: binary DataMessage connection loop. */
	static THREAD_RET THREAD_FUNCTION_CALL MessageConnectionRun(THREAD_ARG arg);
	/*! Thread entry: Telnet server connection loop. */
	static THREAD_RET THREAD_FUNCTION_CALL TelnetServerRun(THREAD_ARG arg);
};


}

#endif // _NETWORKMANAGER_H_
