13 #include <openssl/evp.h>
14 #include <openssl/rsa.h>
15 #include <openssl/x509.h>
16 #include <openssl/x509v3.h>
17 #include <openssl/pem.h>
83 std::map<uint32, NetworkChannel*>::iterator it, itEnd;
85 if (it->second != NULL) {
86 it->second->shutdown();
104 bool createdConnection =
false;
108 channelID = channel->
cid;
113 if (channel == NULL) {
118 channel->
cid = channelID;
121 createdConnection =
true;
125 if (!channel->
startListener(channelID, port, encryption, protocol, isAsync, protocolTimeout, isDefaultProtocol)) {
126 if (createdConnection) {
148NetworkChannel*
NetworkManager::createTCPConnection(
const char* addr, uint16 port, uint8 encryption, uint8 protocol,
bool isAsync,
bool autoreconnect, uint32 channelID,
NetworkReceiver* recv, uint64& conid, uint64& location, uint32 timeoutMS) {
149 bool createdConnection =
false;
153 if (channel == NULL) {
158 channel->
cid = channelID;
160 createdConnection =
true;
164 if ( (conid = channel->
createTCPConnection(addr, port, encryption, protocol, isAsync, autoreconnect, location, timeoutMS)) == 0) {
165 if (createdConnection) {
183 bool createdConnection =
false;
187 if (channel == NULL) {
190 if (!con->
connect(location, timeoutMS, NULL)) {
198 channel->
cid = channelID;
199 if ( (conid = channel->
startConnection(con, protocol, isAsync, autoreconnect)) == 0) {
204 createdConnection =
true;
208 if ( (conid = channel->
createTCPConnection(location, encryption, protocol, isAsync, autoreconnect, timeoutMS)) == 0) {
209 if (createdConnection) {
227NetworkChannel*
NetworkManager::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) {
228 bool createdConnection =
false;
232 if (channel == NULL) {
237 channel->
cid = channelID;
239 createdConnection =
true;
243 if ( (conid = channel->
createTCPConnection(addresses, addressCount, port, encryption, protocol, isAsync, autoreconnect, location, timeoutMS)) == 0) {
244 if (createdConnection) {
262 bool createdConnection =
false;
266 if (channel == NULL) {
271 channel->
cid = channelID;
273 createdConnection =
true;
278 if (createdConnection) {
295 bool createdConnection =
false;
299 if (channel == NULL) {
304 channel->
cid = channelID;
306 createdConnection =
true;
311 if (createdConnection) {
328NetworkChannel*
NetworkManager::addTCPConnection(
const char* addr, uint16 port, uint8 encryption, uint8 protocol,
bool isAsync, uint32 channelID,
NetworkReceiver* recv, uint64& conid, uint64& location, uint32 timeoutMS,
const char* greetingData, uint32 greetingSize) {
329 bool createdConnection =
false;
333 if (channel == NULL) {
338 channel->
cid = channelID;
340 createdConnection =
true;
344 if ((conid = channel->
addTCPConnection(addr, port, encryption, protocol, isAsync, location, timeoutMS, greetingData, greetingSize)) == 0) {
345 if (createdConnection) {
363 bool createdConnection =
false;
367 if (channel == NULL) {
372 channel->
cid = channelID;
374 createdConnection =
true;
378 if ((conid = channel->
addTCPConnection(location, encryption, protocol, isAsync, timeoutMS, greetingData, greetingSize)) == 0) {
379 if (createdConnection) {
399 bool createdConnection =
false;
403 if (channel == NULL) {
408 channel->
cid = channelID;
410 createdConnection =
true;
414 if ( (conid = channel->
createUDPConnection(port, protocol, isAsync, autoreconnect)) == 0) {
415 if (createdConnection) {
504 int8 encryption =
NOENC;
505 if (protocolString ==
"http") {}
506 else if (protocolString ==
"https")
512 if (!hostString.size())
524 if (!uriString.size())
528 if (content && contentSize)
541 uint64 conid, location;
561 std::map<std::string, std::string>& headerEntries,
const char* content,
const char* contentType, uint32 contentSize,
562 bool keepAlive, uint64 ifModifiedSince) {
567 uint64 conid, location;
574 if (!host.length() || !protocol.length())
577 int8 encryption =
NOENC;
578 if (
stricmp(protocol.c_str(),
"https") == 0) {
614 std::map<std::string, std::string>& headerEntries, std::map<std::string, HTTPPostEntry*>& bodyEntries,
615 bool keepAlive, uint64 ifModifiedSince) {
620 uint64 conid, location;
627 if (!host.length() || !protocol.length())
630 int8 encryption =
NOENC;
631 if (
stricmp(protocol.c_str(),
"https") == 0) {
642 if (bodyEntries.size() > 1) {
645 else if (bodyEntries.size() == 1) {
718 std::map<uint16, NetworkThread*>::iterator it, itEnd;
724 std::vector<NetworkThread*> stopListeners;
725 std::vector<NetworkThread*> stopConnections;
729 if ( (thread = it->second) != NULL) {
731 stopListeners.push_back(thread);
736 std::map<uint64, NetworkThread*>::iterator it2, it2End;
738 if ( (thread = it2->second) != NULL) {
741 stopConnections.push_back(thread);
749 for (
size_t i = 0; i < stopListeners.size(); i++) {
750 thread = stopListeners[i];
751 for (
int waited = 0; thread->
isRunning && waited < 200; waited += 5)
759 for (
size_t i = 0; i < stopConnections.size(); i++) {
760 thread = stopConnections[i];
761 for (
int waited = 0; thread->
isRunning && waited < 1000; waited += 5)
822 bool created =
false;
823 if (thread == NULL) {
826 conid =
manager->addConnection(
this);
832 if (
manager->sslCertPath.size())
842 if (isDefaultProtocol)
846 if ( isDefaultProtocol && (protocolTimeout == 0) )
864 if (isDefaultProtocol)
868 if ( isDefaultProtocol && (protocolTimeout == 0) )
881 if (thread == NULL) {
904 if (encryption ==
NOENC) {
906 if (!tcpCon->
connect(addr, port, location, timeoutMS, NULL)) {
912 else if (encryption ==
SSLENC) {
915 if (!sslCon->
init()) {
919 if (!sslCon->
connect(addr, port, location, timeoutMS, NULL)) {
925 return startConnection(con, protocol, isAsync, autoreconnect, timeoutMS);
930 if (encryption ==
NOENC) {
932 if (!tcpCon->
connect(location, timeoutMS, NULL)) {
938 else if (encryption ==
SSLENC) {
941 if (!sslCon->
connect(location, timeoutMS, NULL)) {
947 return startConnection(con, protocol, isAsync, autoreconnect, timeoutMS);
950uint64
NetworkChannel::createTCPConnection(
const uint32* addresses, uint16 addressCount, uint16 port, uint8 encryption, uint8 protocol,
bool isAsync,
bool autoreconnect, uint64& location, uint32 timeoutMS) {
952 if (encryption ==
NOENC) {
954 if (!tcpCon->
connect(addresses, addressCount, port, location, timeoutMS, NULL)) {
960 else if (encryption ==
SSLENC) {
963 if (!sslCon->
connect(addresses, addressCount, port, location, timeoutMS, NULL)) {
969 return startConnection(con, protocol, isAsync, autoreconnect, timeoutMS);
972uint64
NetworkChannel::addTCPConnection(
const char* addr, uint16 port, uint8 encryption, uint8 protocol,
bool isAsync, uint64& location, uint32 timeoutMS,
const char* greetingData, uint32 greetingSize) {
974 if (encryption ==
NOENC) {
976 if (greetingData && greetingSize)
978 if (!tcpCon->
delayedConnect(addr, port, location, timeoutMS, NULL)) {
984 else if (encryption ==
SSLENC) {
987 if (!sslCon->
init()) {
991 if (greetingData && greetingSize)
993 if (!sslCon->
delayedConnect(addr, port, location, timeoutMS, NULL)) {
1004 if (encryption ==
NOENC) {
1006 if (greetingData && greetingSize)
1014 else if (encryption ==
SSLENC) {
1017 if (!sslCon->
init()) {
1021 if (greetingData && greetingSize)
1038 int8 encryption =
NOENC;
1039 if (protocolString ==
"http") {}
1040 else if (protocolString ==
"https")
1046 if (!hostString.size())
1051 if (encryption ==
SSLENC)
1058 if (!uriString.size())
1061 return createWebsocketConnection(uriString.c_str(), hostString.c_str(), port, encryption, protocolName, origin, timeoutMS);
1068 if (encryption ==
NOENC) {
1070 if (!tcpCon->
connect(addr, port, location, timeoutMS, NULL)) {
1076 else if (encryption ==
SSLENC) {
1079 if (!sslCon->
connect(addr, port, location, timeoutMS, NULL)) {
1117 uint64 conid =
startConnection(con, protocol, isAsync, autoreconnect);
1123 if (thread == NULL) {
1150 if (!thread || !thread->
con)
1157 if (!thread || !thread->
con)
1165 if (thread == NULL) {
1172 uint32 timeleft = 200;
1175 if ( (timeleft -= 5) <= 0)
1186 manager->removeConnection(conid);
1310 if ((thread == NULL) || (thread->
con == NULL))
1318 if ((thread == NULL) || (thread->
con == NULL))
1326 if ((thread == NULL) || (thread->
con == NULL))
1334 if ((thread == NULL) || (thread->
con == NULL))
1353 if ((thread == NULL) || (thread->
con == NULL))
1363 if ((thread == NULL) || (thread->
con == NULL))
1375 if ((thread == NULL) || (thread->
con == NULL))
1382 if ((thread == NULL) || (thread->
con == NULL))
1389 if ((thread == NULL) || (thread->
con == NULL))
1482 uint64 conid =
manager->addConnection(
this);
1515 uint64 conid =
manager->addConnection(
this);
1560 if ((thread == NULL) || (thread->
listener == NULL))
1584 if ((thread == NULL) || (thread->
con == NULL))
1590 uint32 maxSize = 1024;
1591 char* buffer =
new char[maxSize];
1601 delete(thread->
con);
1636 delete(thread->
con);
1642 if (protocol == 0) {
1644 LogPrint(0,
LOG_NETWORK,2,
"No valid protocol detected for incoming network connection, disconnecting...\n\n");
1646 delete(thread->
con);
1688 delete(thread->
con);
1701 if ((thread == NULL) || (thread->
con == NULL))
1705 bool disconnected =
false;
1707 bool upgradedToWebsocket =
false;
1717 if (upgradedToWebsocket) {
1744 upgradedToWebsocket =
true;
1756 if (!disconnected) {
1758 disconnected =
true;
1772 disconnected =
false;
1800 if ((thread == NULL) || (thread->
con == NULL))
1804 bool disconnected =
false;
1806 bool upgradedToWebsocket =
false;
1807 std::string wsOrigin;
1815 if (upgradedToWebsocket) {
1823 delete(wsDataReply);
1845 const char* version = req->
getHeaderEntry(
"Sec-WebSocket-Version");
1846 if (key && version) {
1860 upgradedToWebsocket =
true;
1866 LogPrint(0,
LOG_NETWORK, 1,
"Unable to upgrade HTTP Server %llu to Websocket, key and/or version not provided", thread->
id);
1911 if ((thread == NULL) || (thread->
con == NULL))
1915 bool disconnected =
false;
1916 bool wasConnected =
false;
1923 wasConnected =
true;
1942 if (!disconnected && wasConnected) {
1944 disconnected =
true;
1978 disconnected =
false;
2002 if ((thread == NULL) || (thread->
con == NULL))
2006 bool disconnected =
false;
2049 const uint16 PORT = 38101;
2050 const uint32 CONNECTIONS = 3;
2058 unittest::fail(
"NetworkManager delayed-connect test: could not start listening on port %u", PORT);
2065 msgConnect->
setString(
"URI",
"ExecutorConnect");
2067 std::vector<uint64> conIDs;
2069 uint64 location = 0;
2073 channel = manager->
addTCPConnection(
"localhost", PORT,
NOENC,
PROTOCOL_MESSAGE,
true, 0, NULL, conid, location, 1000, (
char*)msgConnect->
data, msgConnect->
getSize());
2074 if (!channel || !conid) {
2075 unittest::fail(
"NetworkManager delayed-connect test: could not create channel for connection 1");
2078 conIDs.push_back(conid);
2079 for (uint32 n = 1; n < CONNECTIONS; n++) {
2082 unittest::fail(
"NetworkManager delayed-connect test: could not add connection %u", n + 1);
2085 conIDs.push_back(conid);
2089 for (uint32 n = 0; n < CONNECTIONS; n++) {
2093 unittest::fail(
"NetworkManager delayed-connect test: greeting %u not received", n + 1);
2116 uint32 size = 70000;
2125 uint32 count = 100, subcount = 100, subcount2 = 10, n, m, k;
2128 PsyType type = { { 1,10001,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } };
2143 for (
int tries = 0; tries < 20 && !listen; tries++) {
2148 unittest::fail(
"NetworkManager test: could not start listening on port 38100");
2158 dat =
new char[size];
2159 memset(dat, 0, size);
2161 msg->
setData(
"Test", dat, size);
2173 unittest::fail(
"NetworkManager test: message sent and received mismatch");
2192 unittest::fail(
"NetworkManager test: message sent and received mismatch");
2204 count = 10, subcount = 10, subcount2 = 15;
2205 for (m = 0; m < count; m++) {
2208 for (n = 0; n < subcount; n++) {
2209 type.levels[15] = n;
2212 for (k=0; k<subcount2; k++) {
2214 unittest::fail(
"NetworkManager test: could not send TCP message %u", n);
2224 for (k=0; k<subcount2; k++) {
2226 type.levels[15] = n;
2228 unittest::fail(
"NetworkManager test: [%u/%u/%u] no TCP message received", n, m, k);
2232 unittest::fail(
"NetworkManager test: [%u] TCP message sent and received mismatch", n);
2239 unittest::detail(
"[%u/%u] Sent and received %u msgs (%u bytes), %.3fus per msg",
2240 (m+1)*subcount*subcount2, (m+1)*subcount*subcount2 * size,
2241 subcount*subcount2, subcount*subcount2 * size,
2242 ((
double)(end-start))/(subcount*subcount2));
2247 uint32 tcpMsgs = count*subcount*subcount2;
2248 double tcpUs = (double)t;
2249 unittest::detail(
"Total: Sent and received %u TCP msgs, %.3fus per msg", tcpMsgs, tcpUs/tcpMsgs);
2250 unittest::metric(
"tcp_msg_throughput", (
double)tcpMsgs / tcpUs * 1e6,
"msg/s",
true);
2252 unittest::metric(
"tcp_throughput", ((
double)tcpMsgs * size) / tcpUs,
"MB/s",
true);
2263 unittest::fail(
"NetworkManager test: could not start UDP listening on port 38101");
2275 dat =
new char[size];
2276 memset(dat, 0, size);
2278 msg->
setData(
"Test", dat, size);
2288 for (
int attempt = 0; attempt < 10 && !udpOk; attempt++) {
2290 unittest::fail(
"NetworkManager test: could not send UDP message");
2298 unittest::fail(
"NetworkManager test: UDP message sent and received mismatch");
2305 unittest::fail(
"NetworkManager test: no UDP round-trip on port 38101 after 10 attempts (loopback UDP may be blocked here)");
2318 count = 10, subcount = 10, subcount2 = 15;
2321 const int32 udpBudgetMs = 5000;
2322 for (m = 0; m < count &&
GetTimeAgeMS(udpLoopStart) < udpBudgetMs; m++) {
2325 for (n = 0; n < subcount; n++) {
2326 type.levels[15] = n;
2329 for (k=0; k<subcount2; k++) {
2331 unittest::fail(
"NetworkManager test: could not send UDP message %u", n);
2341 for (k=0; k<subcount2; k++) {
2353 unittest::detail(
"[%u/%u] Sent and received %u msgs (%u bytes), %.3fus per msg",
2354 (m+1)*subcount*subcount2, (m+1)*subcount*subcount2 * size,
2355 subcount*subcount2, subcount*subcount2 * size,
2356 ((
double)(end-start))/(subcount*subcount2));
2363 uint32 udpMsgs = count*subcount*subcount2;
2364 double udpUs = (double)t;
2366 unittest::detail(
"Total UDP: Sent and received %u msgs, %.3fus per msg", udpMsgs, udpUs/udpMsgs);
2367 unittest::metric(
"udp_msg_throughput", (
double)udpMsgs / udpUs * 1e6,
"msg/s",
true);
2403 snprintf(text, 512,
"Hello World %llu",
GetTimeNow());
2405 uint64 localAddr = 0;
2410 printf(
"Error generating HTML page...\n\n");
2414 printf(
"Error sending response...\n\n");
2436 printf(
"WebsocketTestServer received HTTPRequest...\n\n");
2447 wsDataOut->
setData(wsData->
TEXT,
false, str.c_str(), str.length());
2501 uint64 httpStart, httpEnd;
2510 unittest::fail(
"NetworkManager HTTP test: could not start listening on port 38102");
2525 unittest::fail(
"NetworkManager HTTP test: could not create request");
2530 if (reply == NULL) {
2531 unittest::fail(
"NetworkManager HTTP test: did not receive reply");
2539 for (n=0; n<count; n++) {
2544 unittest::fail(
"NetworkManager HTTP test: could not reconnect [%u]", n);
2552 unittest::fail(
"NetworkManager HTTP test: could not create request [%u]", n);
2557 if (reply == NULL) {
2558 unittest::fail(
"NetworkManager HTTP test: did not receive reply [%u]", n);
2565 double httpUs = (double)(httpEnd - httpStart);
2567 unittest::metric(
"http_request_rate", (
double)count / httpUs * 1e6,
"req/s",
true);
2629static bool generateSelfSignedCert(
const char* certPath,
const char* keyPath) {
2631 EVP_PKEY* pkey = EVP_RSA_gen(2048);
2634 X509* x509 = X509_new();
2636 ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);
2637 X509_gmtime_adj(X509_getm_notBefore(x509), 0);
2638 X509_gmtime_adj(X509_getm_notAfter(x509), (
long)60 * 60 * 24 * 3650);
2639 X509_set_pubkey(x509, pkey);
2640 X509_NAME* name = X509_get_subject_name(x509);
2641 X509_NAME_add_entry_by_txt(name,
"CN", MBSTRING_ASC,
2642 (
const unsigned char*)
"localhost", -1, -1, 0);
2643 X509_set_issuer_name(x509, name);
2644 if (X509_sign(x509, pkey, EVP_sha256())) {
2645 FILE* kf = fopen(keyPath,
"wb");
2646 FILE* cf = fopen(certPath,
"wb");
2648 PEM_write_PrivateKey(kf, pkey, NULL, NULL, 0, NULL, NULL) == 1 &&
2649 PEM_write_X509(cf, x509) == 1)
2656 EVP_PKEY_free(pkey);
2664bool NetworkManager::UnitTestHTTPS() {
2672 uint64 httpsStart, httpsEnd;
2674 const char* certPath =
"/tmp/psytest_https_cert.pem";
2675 const char* keyPath =
"/tmp/psytest_https_key.pem";
2678 if (!generateSelfSignedCert(certPath, keyPath)) {
2679 unittest::fail(
"NetworkManager HTTPS test: could not generate self-signed certificate");
2683 HTTPTestServer* testServer =
new HTTPTestServer();
2685 manager->setSSLCertificate(certPath, keyPath);
2689 manager->setSSLAllowSelfSigned(
true);
2694 unittest::fail(
"NetworkManager HTTPS test: could not start SSL listener on port 38112");
2701 unittest::fail(
"NetworkManager HTTPS test: could not connect / TLS handshake failed");
2706 req =
new HTTPRequest((uint64)0);
2707 if (!req->createRequest(
HTTP_GET,
"",
"/", NULL, 0,
true, 0)) {
2709 unittest::fail(
"NetworkManager HTTPS test: could not create request");
2712 reply = con->sendReceiveHTTPRequest(req, conid, 5000);
2714 if (reply == NULL) {
2715 unittest::fail(
"NetworkManager HTTPS test: did not receive reply over TLS");
2723 for (n=0; n<count; n++) {
2725 con->endConnection(conid);
2728 unittest::fail(
"NetworkManager HTTPS test: could not reconnect over TLS [%u]", n);
2732 req =
new HTTPRequest((uint64)0);
2733 if (!req->createRequest(
HTTP_GET,
"",
"/", NULL, 0,
true, 0)) {
2735 unittest::fail(
"NetworkManager HTTPS test: could not create request [%u]", n);
2738 reply = con->sendReceiveHTTPRequest(req, conid, 5000);
2740 if (reply == NULL) {
2741 unittest::fail(
"NetworkManager HTTPS test: did not receive reply over TLS [%u]", n);
2748 double httpsUs = (double)(httpsEnd - httpsStart);
2749 unittest::detail(
"HTTPS: %u TLS requests in %.3fms", count, httpsUs / 1000.0);
2750 unittest::metric(
"https_request_rate", (
double)count / httpsUs * 1e6,
"req/s",
true);
2753 con->endConnection(conid);
2780bool NetworkManager::UnitTestSSLVerify() {
2782 const char* certPath =
"/tmp/psytest_sslverify_cert.pem";
2783 const char* keyPath =
"/tmp/psytest_sslverify_key.pem";
2784 uint64 conid = 0, location = 0;
2790 SSLConnection* c1 =
new SSLConnection();
2791 if (!c1->init() || (c1->getVerifyMode() != SSL_VERIFY_PEER)) {
2792 unittest::fail(
"default client ctx verify mode is not SSL_VERIFY_PEER (got %d)", c1->getVerifyMode());
2797 SSLConnection* c2 =
new SSLConnection();
2798 c2->setAllowSelfSigned(
true);
2799 if (!c2->init() || (c2->getVerifyMode() != SSL_VERIFY_NONE)) {
2800 unittest::fail(
"allowselfsigned client ctx verify mode is not SSL_VERIFY_NONE (got %d)", c2->getVerifyMode());
2805 unittest::detail(
"ctx verify modes correct (PEER by default, NONE when allowselfsigned)");
2809 if (!generateSelfSignedCert(certPath, keyPath)) {
2814 HTTPTestServer* testServer =
new HTTPTestServer();
2816 manager->setSSLCertificate(certPath, keyPath);
2828 con = manager->createTCPConnection(
"localhost", 38113,
SSLENC,
PROTOCOL_HTTP_CLIENT,
true,
false, 0, NULL, conid, location, 3000);
2830 unittest::fail(
"connection to untrusted self-signed server was ACCEPTED with verification on");
2831 con->endConnection(conid);
2835 unittest::detail(
"verification on: TLS handshake to self-signed server correctly rejected");
2838 manager->setSSLAllowSelfSigned(
true);
2839 con = manager->createTCPConnection(
"localhost", 38113,
SSLENC,
PROTOCOL_HTTP_CLIENT,
true,
false, 0, NULL, conid, location, 3000);
2841 unittest::fail(
"connection to self-signed server FAILED despite allowselfsigned=true");
2845 unittest::detail(
"allowselfsigned: TLS handshake to self-signed server accepted");
2846 con->endConnection(conid);
2863static EVP_PKEY* sslTestGenKey() {
2864 return EVP_RSA_gen(2048);
2867static bool sslTestWritePEM(X509* cert, EVP_PKEY* key,
const char* certPath,
const char* keyPath) {
2869 FILE* cf = fopen(certPath,
"wb");
2870 FILE* kf = keyPath ? fopen(keyPath,
"wb") : NULL;
2871 if (cf && PEM_write_X509(cf, cert) == 1)
2874 ok = (kf && PEM_write_PrivateKey(kf, key, NULL, NULL, 0, NULL, NULL) == 1);
2880static bool sslTestAddExt(X509* cert, X509* issuer,
int nid,
const char* value) {
2882 X509V3_set_ctx_nodb(&ctx);
2883 X509V3_set_ctx(&ctx, issuer, cert, NULL, NULL, 0);
2884 X509_EXTENSION* ext = X509V3_EXT_conf_nid(NULL, &ctx, nid, value);
2887 X509_add_ext(cert, ext, -1);
2888 X509_EXTENSION_free(ext);
2893static bool sslTestGenCA(
const char* caCertPath, EVP_PKEY** caKeyOut, X509** caCertOut) {
2894 EVP_PKEY* key = sslTestGenKey();
2897 X509* x = X509_new();
2898 if (!x) { EVP_PKEY_free(key);
return false; }
2899 X509_set_version(x, 2);
2900 ASN1_INTEGER_set(X509_get_serialNumber(x), 1000);
2901 X509_gmtime_adj(X509_getm_notBefore(x), 0);
2902 X509_gmtime_adj(X509_getm_notAfter(x), (
long)60 * 60 * 24 * 365);
2903 X509_set_pubkey(x, key);
2904 X509_NAME* name = X509_get_subject_name(x);
2905 X509_NAME_add_entry_by_txt(name,
"CN", MBSTRING_ASC,
2906 (
const unsigned char*)
"Psyclone Test CA", -1, -1, 0);
2907 X509_set_issuer_name(x, name);
2908 bool ok = sslTestAddExt(x, x, NID_basic_constraints,
"critical,CA:TRUE") &&
2909 sslTestAddExt(x, x, NID_key_usage,
"critical,keyCertSign,cRLSign") &&
2910 (X509_sign(x, key, EVP_sha256()) != 0) &&
2911 sslTestWritePEM(x, NULL, caCertPath, NULL);
2912 if (!ok) { X509_free(x); EVP_PKEY_free(key);
return false; }
2919static bool sslTestGenLeaf(
const char* certPath,
const char* keyPath,
const char* cn,
2920 const char* san, X509* caCert, EVP_PKEY* caKey,
long serial) {
2921 EVP_PKEY* key = sslTestGenKey();
2924 X509* x = X509_new();
2925 if (!x) { EVP_PKEY_free(key);
return false; }
2926 X509_set_version(x, 2);
2927 ASN1_INTEGER_set(X509_get_serialNumber(x), serial);
2928 X509_gmtime_adj(X509_getm_notBefore(x), 0);
2929 X509_gmtime_adj(X509_getm_notAfter(x), (
long)60 * 60 * 24 * 365);
2930 X509_set_pubkey(x, key);
2931 X509_NAME* name = X509_get_subject_name(x);
2932 X509_NAME_add_entry_by_txt(name,
"CN", MBSTRING_ASC,
2933 (
const unsigned char*)cn, -1, -1, 0);
2934 X509_set_issuer_name(x, X509_get_subject_name(caCert));
2935 bool ok = sslTestAddExt(x, caCert, NID_basic_constraints,
"critical,CA:FALSE") &&
2936 sslTestAddExt(x, caCert, NID_subject_alt_name, san) &&
2937 (X509_sign(x, caKey, EVP_sha256()) != 0) &&
2938 sslTestWritePEM(x, key, certPath, keyPath);
2952bool NetworkManager::UnitTestSSLHostCA() {
2954 const char* caCertPath =
"/tmp/psytest_sslhostca_ca.pem";
2955 const char* goodCertPath =
"/tmp/psytest_sslhostca_good_cert.pem";
2956 const char* goodKeyPath =
"/tmp/psytest_sslhostca_good_key.pem";
2957 const char* badCertPath =
"/tmp/psytest_sslhostca_bad_cert.pem";
2958 const char* badKeyPath =
"/tmp/psytest_sslhostca_bad_key.pem";
2959 EVP_PKEY* caKey = NULL;
2960 X509* caCert = NULL;
2961 uint64 conid = 0, location = 0;
2963 HTTPTestServer* testServer = NULL;
2970 if (!sslTestGenCA(caCertPath, &caKey, &caCert)) {
2974 if (!sslTestGenLeaf(goodCertPath, goodKeyPath,
"localhost",
"DNS:localhost", caCert, caKey, 1001) ||
2975 !sslTestGenLeaf(badCertPath, badKeyPath,
"wronghost.example",
"DNS:wronghost.example", caCert, caKey, 1002)) {
2976 unittest::fail(
"could not generate CA-signed server certificates");
2977 X509_free(caCert); EVP_PKEY_free(caKey);
2981 unittest::detail(
"CA + leaf certs generated (SAN localhost / wronghost.example)");
2983 testServer =
new HTTPTestServer();
2987 goodManager->setSSLCertificate(goodCertPath, goodKeyPath);
2996 unittest::progress(30,
"connecting with verify on, custom CA NOT provided (must be rejected)");
2997 con = goodManager->createTCPConnection(
"localhost", 38114,
SSLENC,
PROTOCOL_HTTP_CLIENT,
true,
false, 0, NULL, conid, location, 3000);
2999 unittest::fail(
"CA-signed server was ACCEPTED although its CA was not provided");
3000 con->endConnection(conid);
3006 unittest::progress(45,
"connecting with custom CA provided + matching hostname (must be accepted)");
3007 goodManager->setSSLCALocation(caCertPath, NULL);
3008 con = goodManager->createTCPConnection(
"localhost", 38114,
SSLENC,
PROTOCOL_HTTP_CLIENT,
true,
false, 0, NULL, conid, location, 3000);
3010 unittest::fail(
"connection FAILED despite custom CA provided and matching hostname");
3015 con->endConnection(conid);
3019 badManager->setSSLCertificate(badCertPath, badKeyPath);
3020 badManager->setSSLCALocation(caCertPath, NULL);
3029 unittest::progress(70,
"connecting by hostname to server with mismatching cert (must be rejected)");
3030 con = badManager->createTCPConnection(
"localhost", 38115,
SSLENC,
PROTOCOL_HTTP_CLIENT,
true,
false, 0, NULL, conid, location, 3000);
3032 unittest::fail(
"server with cert for wronghost.example was ACCEPTED for hostname localhost");
3033 con->endConnection(conid);
3037 unittest::detail(
"hostname mismatch: trusted cert for wrong host correctly rejected");
3039 unittest::progress(85,
"connecting by IP literal (no hostname check; must be accepted)");
3040 con = badManager->createTCPConnection(
"127.0.0.1", 38115,
SSLENC,
PROTOCOL_HTTP_CLIENT,
true,
false, 0, NULL, conid, location, 3000);
3042 unittest::fail(
"connection by IP literal FAILED (chain-of-trust only path broken)");
3046 unittest::detail(
"IP literal connect: chain-of-trust only, accepted (hostname check skipped)");
3047 con->endConnection(conid);
3051 delete(goodManager);
3056 EVP_PKEY_free(caKey);
3058 unlink(goodCertPath); unlink(goodKeyPath);
3059 unlink(badCertPath); unlink(badKeyPath);
3088 printf(
"Connecting...\n");
3091 printf(
"Could not connect to host %s:%u...\n\n", data->
host, data->
port);
3097 for (uint32 n=0; n<10; n++) {
3105 printf(
"Could not create request [%u]...\n\n", n);
3110 printf(
"[%u] Sending...\n", n);
3114 if (reply == NULL) {
3115 printf(
"Did not receive reply...\n\n");
3120 printf(
"Server unavailable...\n\n");
3125 printf(
"Server no reply...\n\n");
3130 printf(
"Server malformed reply...\n\n");
3162 unittest::fail(
"NetworkManager websocket test: could not start listening on port 38103");
3170 unittest::fail(
"NetworkManager websocket test: could not connect");
3178 unittest::fail(
"NetworkManager websocket test: could not create websocket upgrade request");
3183 if (reply == NULL) {
3184 unittest::fail(
"NetworkManager websocket test: did not receive handshake reply");
3189 unittest::fail(
"NetworkManager websocket test: reply was not a websocket upgrade");
3214 printf(
"Testing HTTP on %s:%u with random urls...\n\n", host, port);
3217 uint32 numThreads = 5;
3226 printf(
"Could not connect to host %s:%u...\n\n", host, port);
3231 printf(
"Host is available, starting test...\n\n");
3295 uint32 threadIDs[1000];
3297 for (n=0; n<loops; n++) {
3298 for (m=0; m<numThreads; m++) {
3299 testData[m].
host = host;
3300 testData[m].
port = port;
3301 testData[m].
urls = &urls;
3302 testData[m].
manager = manager;
3310 for (m=0; m<numThreads; m++) {
3313 if (testData[m].status > 10) {
3314 printf(
"[%u] Failed\n", m);
3318 printf(
"[%u] Done\n", m);
3321 if (m >= numThreads)
3333 "TCP and UDP message send/receive over loopback",
"network");
3335 "Delayed/greeting TCP connections to a loopback listener",
"network");
3337 "HTTP server/client request/reply over loopback",
"network");
3339 "Websocket upgrade handshake against loopback server",
"network");
3344 "HTTPS (TLS) server/client request/reply over loopback",
"network");
3346 "SSL client certificate verification (secure by default, allowselfsigned opt-out)",
"network");
3348 "SSL hostname verification (SSL_set1_host) and custom CA location (SSL_CTX_load_verify_locations)",
"network");
#define NETWORKERROR_GREETING_ERROR
The initial greeting/handshake data exchange failed.
#define NOENC
Plain, unencrypted transport.
#define SSLENC
SSL/TLS encryption (requires build with _USE_SSL_).
Connection/channel management layer: multi-protocol listeners, typed dispatch, HTTP client — and the ...
#define NETWORKEVENT_PROTOCOL_ERROR
Protocol parsing/framing error on the connection.
#define NETWORKEVENT_CONNECT
Connection established.
#define NETWORKEVENT_DISCONNECT_RETRYING
Connection lost; reconnection attempts in progress.
#define NETWORKEVENT_DISCONNECT
Connection closed for good.
#define NETWORKEVENT_RECONNECT
Connection re-established after a failure (autoreconnect).
#define NETWORKEVENT_UNPROCESSED_DATA
Bytes arrived that no protocol handler consumed.
#define HTTP_MALFORMED_URL
400 Bad Request
#define PROTOCOL_TELNET
Line-based Telnet-style text protocol.
#define HTTP_SERVER_MALFORMED_REPLY
500 (malformed backend reply)
#define PROTOCOL_HTTP_CLIENT
Speak HTTP as a client: send requests, parse replies.
#define HTTP_SWITCH_PROTOCOL
101 Switching Protocols (WebSocket upgrade)
#define HTTP_ACCESS_DENIED
403 Forbidden
#define HTTP_SERVER_NOREPLY
500 (no reply from backend server)
#define PROTOCOL_HTTP_SERVER
Serve HTTP: parse requests, send replies (server role).
#define HTTP_NOT_IMPLEMENTED
501 Not Implemented
#define PROTOCOL_MESSAGE
CMSDK binary DataMessage protocol (size-prefixed frames).
#define HTTP_SERVER_UNAVAILABLE
500 (backend server unavailable)
Small, dependency-free unit test harness used by all CMSDK object tests.
#define thread_ret_val(ret)
#define THREAD_FUNCTION_CALL
THREAD_RET(* THREAD_FUNCTION)(void *)
The central Psyclone data container: a self-contained binary message with typed, named user entries.
uint32 getFrom()
getFrom() Get the sender id
bool setString(const char *key, const char *value)
setString(const char* key, const char* value)
DataMessageHeader * data
Pointer to the message's flat memory block (header + user entries).
PsyType getType()
getType()
uint32 getSize()
getSize() Get message size Many types of data of any size can be put into a message as user entries; ...
bool setType(PsyType &type)
setType(PsyType &type)
bool setData(const char *key, const char *value, uint32 size)
setData(const char* key, const char* value, uint32 size)
static bool SendHTTPReply(NetworkConnection *con, HTTPReply *reply)
Serialise and send a reply.
static HTTPReply * ReceiveHTTPReply(NetworkConnection *con, uint32 timeout)
Read a full reply from the connection.
static bool SendHTTPRequest(NetworkConnection *con, HTTPRequest *req)
Serialise and send a request.
static HTTPRequest * ReceiveHTTPRequest(NetworkConnection *con, uint32 timeout)
Read a full request from the connection.
static bool CheckBufferForCompatibility(const char *buffer, uint32 length)
static WebsocketData * ReceiveWebsocketData(NetworkConnection *con, uint32 timeout)
Read one complete WebSocket message (reassembling fragments).
static bool SendWebsocketData(NetworkConnection *con, WebsocketData *wsData)
Send a WebSocket frame.
A parsed or generated HTTP response.
bool isWebsocketUpgrade()
uint8 type
HTTP_* status id of this reply.
static HTTPReply * CreateErrorReply(uint8 type)
Build a canned error reply.
static HTTPReply * CreateWebsocketHTTPReply(const char *key, const char *version)
Build the "101 Switching Protocols" reply for a WebSocket handshake.
bool createPage(uint8 status, uint64 time, const char *serverName, uint64 lastMod, bool keepAlive, bool cache, const char *contentType, const char *content, uint32 contentSize=0, const char *additionalHeaderEntries=NULL)
Build a complete response with headers and content.
A parsed or generated HTTP request (also used for WebSocket upgrade handshakes).
const char * getHeaderEntry(const char *entry)
Look up a header field (case-insensitive).
uint32 headerLength
Length of the header block in data, in bytes.
uint32 contentLength
Body length (from Content-Length / parsing), in bytes.
static HTTPRequest * CreateWebsocketRequest(const char *uri, const char *host, const char *protocolName, const char *origin)
Build a client-side WebSocket upgrade request (RFC 6455 handshake).
bool createWebsocketRequest(const char *uri, const char *host, const char *protocolName, const char *origin)
Fill this object with a WebSocket upgrade handshake request.
bool createRequest(uint8 type, const char *host, const char *uri, const char *content, uint32 contentSize, bool keepAlive, uint64 ifModifiedSince)
Build a simple request with optional raw body.
bool createMultipartRequest(uint8 type, const char *host, const char *uri, std::map< std::string, std::string > &headerEntries, std::map< std::string, HTTPPostEntry * > &bodyEntries, bool keepAlive, uint64 ifModifiedSince)
Build a multipart/form-data request from several named parts.
bool isWebsocketUpgrade()
Minimal HTTP server used by the unit tests: replies with a canned page.
virtual bool receiveHTTPRequest(HTTPRequest *req, NetworkChannel *channel, uint64 conid)
Serve a test page for any request.
static bool SendMessage(NetworkConnection *con, DataMessage *msg, uint64 receiver=0)
Serialise and send one message.
static bool CheckBufferForCompatibility(const char *buffer, uint32 length)
static DataMessage * ReceiveMessage(NetworkConnection *con, uint32 timeout)
Read one full message frame.
One logical network interface: a group of listeners/connections with shared dispatch.
bool setNewReceiver(NetworkReceiver *recv)
Replace the channel's receiver for async dispatch.
bool isConnected(uint64 conid)
static THREAD_RET THREAD_FUNCTION_CALL TelnetServerRun(THREAD_ARG arg)
Thread entry: Telnet server connection loop.
HTTPReply * sendReceiveHTTPRequest(HTTPRequest *req, uint64 conid, uint32 timeout)
Send an HTTP request and block for its reply on the same connection.
NetworkManager * manager
Owning manager (not owned).
std::queue< HTTPReply * > queueHTTPReplies
bool sendHTTPRequest(HTTPRequest *req, uint64 conid)
Send an HTTP request without waiting for the reply (reply arrives via receiveHTTPReply()/waitForHTTPR...
utils::Mutex channelMutex
uint8 getConnectionType(uint64 conid)
HTTPReply * waitForHTTPReply(uint64 &conid, uint32 ms)
Wait for the next queued HTTP reply (sync-mode HTTP client).
bool enterHTTPReply(HTTPReply *reply, HTTPRequest *req, uint64 conid)
Queue or push an incoming HTTP reply.
uint64 getRemoteAddress(uint64 conid)
std::queue< NetworkEvent * > eventQueue
virtual ~NetworkChannel()
NetworkChannel(NetworkManager *manager)
utils::Semaphore eventQueueSemaphore
uint64 createWebsocketConnection(const char *url, const char *protocolName, const char *origin=NULL, uint32 timeoutMS=5000)
Open a client WebSocket connection from a full URL.
uint32 getOutputSpeed(uint64 conid)
std::map< uint16, NetworkThread * > listeners
TCP listener threads by port (owned).
WebsocketData * waitForWebsocketData(uint64 &conid, uint32 ms)
Wait for the next queued WebSocket message.
bool stopListener(uint16 port, uint8 protocol)
Stop a listener on this channel.
utils::Semaphore queueMessagesSemaphore
uint32 getInputSpeed(uint64 conid)
utils::Mutex queueHTTPRequestsMutex
bool enterTelnetLine(TelnetLine *line, uint64 conid)
Queue or push an incoming Telnet line.
static THREAD_RET THREAD_FUNCTION_CALL MessageConnectionRun(THREAD_ARG arg)
Thread entry: binary DataMessage connection loop.
utils::Mutex queueHTTPRepliesMutex
utils::Semaphore queueHTTPRepliesSemaphore
bool endUDPConnection(uint16 port)
Close the UDP connection bound to port.
bool sendMessage(DataMessage *msg, uint64 conid)
Send a DataMessage.
std::map< uint64, NetworkThread * > connectionThreads
Worker threads by connection id (owned).
utils::Semaphore queueTelnetLinesSemaphore
bool enterMessage(DataMessage *msg, uint64 conid)
Queue or push an incoming DataMessage.
utils::Mutex queueWebsocketDataMutex
bool endConnection(uint64 conid)
Gracefully close a connection.
static THREAD_RET THREAD_FUNCTION_CALL ConnectionAutodetectRun(THREAD_ARG arg)
Thread entry: protocol sniffing for a fresh connection (see autoDetectConnection()).
void applySSLClientPolicy(SSLConnection *con)
std::queue< WebsocketData * > queueWebsocketData
TelnetLine * sendReceiveTelnetLine(TelnetLine *line, uint64 conid, uint32 timeout, uint32 size=0)
Send a Telnet line and block for the response line.
friend class NetworkManager
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)
Connect with optional greeting bytes (no autoreconnect); see NetworkManager::addTCPConnection().
uint64 startConnection(NetworkConnection *con, uint8 protocol, bool isAsync, bool autoreconnect, uint32 timeoutMS=5000)
Start the protocol worker thread for an already-connected connection.
bool shutdown()
Stop all worker threads and close all listeners/connections of this channel.
NetworkEvent * waitForNetworkEvent(uint32 ms)
Wait for the next connection lifecycle event (sync mode).
static THREAD_RET THREAD_FUNCTION_CALL HTTPClientRun(THREAD_ARG arg)
Thread entry: HTTP client connection loop (send requests, parse replies).
std::queue< DataMessage * > queueMessages
bool enterNetworkEvent(uint8 type, uint8 protocol, uint64 conid)
Queue or push a lifecycle event.
utils::Mutex queueTelnetLinesMutex
utils::Semaphore queueHTTPRequestsSemaphore
NetworkReceiver * receiver
Async dispatch target (not owned; may be NULL).
std::queue< HTTPRequest * > queueHTTPRequests
bool sendTelnetLine(TelnetLine *line, uint64 conid)
Send a Telnet line.
utils::Semaphore queueWebsocketDataSemaphore
bool sendWebsocketData(WebsocketData *wsData, uint64 conid)
Send a WebSocket frame.
static THREAD_RET THREAD_FUNCTION_CALL NetworkListenerRun(THREAD_ARG arg)
Thread entry: accept loop for a TCP listener.
utils::Mutex eventQueueMutex
utils::Mutex queueMessagesMutex
bool enterHTTPRequest(HTTPRequest *req, uint64 conid)
Queue or push an incoming HTTP request.
std::map< uint16, NetworkThread * > udpListeners
UDP listener threads by port (owned).
bool startListener(uint64 cid, uint16 port, uint8 encryption, uint8 protocol, bool isAsync, uint32 protocolTimeout=3000, bool isDefaultProtocol=false)
Open a listener on this channel (see NetworkManager::createListener() for semantics).
std::queue< uint64 > queueMessageConIDs
uint64 createTCPConnection(const char *addr, uint16 port, uint8 encryption, uint8 protocol, bool isAsync, bool autoreconnect, uint64 &location, uint32 timeoutMS=5000)
Connect to a host by name/IP; see NetworkManager::createTCPConnection().
uint64 autoDetectConnection(NetworkConnection *con, uint16 port, uint32 autoProtocols, uint32 autoProtocolTimeout, uint32 defaultProtocol, bool isAsync, bool autoreconnect)
Adopt an incoming connection whose protocol is not yet known: sniff its first bytes against autoProto...
bool sendHTTPReply(HTTPReply *reply, uint64 conid)
Send an HTTP reply on a server connection.
static THREAD_RET THREAD_FUNCTION_CALL HTTPServerRun(THREAD_ARG arg)
Thread entry: HTTP/WebSocket server connection loop — the built-in web server's per-connection worker...
uint32 cid
Channel id within the manager.
uint64 createUDPConnection(uint16 port, uint8 protocol, bool isAsync, bool autoreconnect)
Bind a UDP port on this channel.
HTTPRequest * waitForHTTPRequest(uint64 &conid, uint32 ms)
Wait for the next queued HTTP request (sync-mode HTTP server).
bool enterWebsocketData(WebsocketData *wsData, uint64 conid)
Queue or push an incoming WebSocket message.
TelnetLine * waitForTelnetLine(uint64 &conid, uint32 ms)
Wait for the next queued Telnet line.
DataMessage * waitForMessage(uint64 &conid, uint32 ms)
Wait for the next queued DataMessage.
std::queue< TelnetLine * > queueTelnetLines
Abstract base class for all point-to-point network connections.
virtual bool send(const char *data, uint32 size, uint64 receiver=0)=0
Send raw bytes on the connection.
virtual uint32 getOutputSpeed()
virtual uint32 clearBuffer()
Discard all currently buffered input.
virtual uint64 getRemoteAddress()
bool setConnectTimeout(uint32 timeoutMS)
Set the timeout used by subsequent connect()/reconnect() attempts.
virtual bool receiveAvailable(char *data, uint32 &size, uint32 maxSize, uint32 timeout, bool peek=false)
Receive whatever bytes are available (up to maxSize).
virtual bool disconnect(uint16 error=0)
Close the connection and release the socket.
virtual uint8 getConnectionType()
virtual uint32 getInputSpeed()
char * greetingData
Owned copy of the greeting bytes (NULL if unset).
virtual bool isConnected(int timeout=0)
Test whether the connection is currently alive.
bool setGreetingData(const char *data, uint32 size)
Set greeting bytes sent automatically right after a connection is established (used e....
virtual bool reconnect(uint32 timeoutMS)=0
Re-establish the connection to the previously known remote endpoint.
uint32 greetingSize
Size of greetingData in bytes.
Central owner of all channels, listeners and connections in a process.
static bool WebsocketTest()
WebSocket upgrade + echo self-test.
friend class NetworkChannel
utils::Mutex udpOutputConMutex
Serialises use of udpOutputCon.
uint64 lastConnectionID
Last connection id issued.
bool endConnection(uint64 conid)
Gracefully close a connection (thread is asked to finish; entry kept for reuse).
uint8 getConnectionType(uint64 conid)
NetworkChannel * getUDPConnectionByPort(uint16 port)
bool endUDPConnection(uint16 port)
Close the UDP connection bound to port.
std::map< uint64, NetworkChannel * > channelsByConnection
Channel lookup by connection id.
static bool UnitTestHTTP()
Self-test of the built-in HTTP server and client.
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)
Like createTCPConnection() but sends optional greeting bytes right after connecting (peer identificat...
UDPConnection * udpOutputCon
Shared output-only UDP socket for sendUDPMessage().
static bool TestHTTP(const char *host, uint32 port, std::vector< std::string > &urls)
Fetch a list of URLs from a host and report results (manual test helper).
std::string sslKeyPath
PEM private key path for SSL listeners.
static bool UnitTest()
Basic TCP/message-protocol round-trip self-test.
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 remote host by name/IP and start the protocol thread.
bool stopListener(uint16 port, uint8 protocol)
Stop a listener previously opened with createListener().
THREAD_RET THREAD_FUNCTION_CALL NetworkManagerRun(THREAD_ARG arg)
Thread entry point of the manager's supervision loop (do not call directly).
bool setSSLCertificate(const char *sslCertPath, const char *sslKeyPath)
Set the certificate/key used by SSL listeners created via this manager.
std::string sslCertPath
PEM certificate path for SSL listeners.
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 a full URL (ws:// or wss://).
uint32 lastChannelID
Last channel id issued.
NetworkChannel * getConnection(uint64 conid)
NetworkChannel * getTCPConnectionByPort(uint16 port)
uint64 addConnection(NetworkChannel *channel)
Register an externally created channel with the manager.
NetworkChannel * createListener(uint16 port, uint8 encryption, uint8 protocol, bool isAsync, uint32 protocolTimeout, bool isDefaultProtocol, uint32 channelID, NetworkReceiver *recv)
Open a listening port for one or more protocols.
uint64 getRemoteAddress(uint64 conid)
HTTPReply * makeHTTPRequest(const char *url, uint32 timeout, const char *content=NULL, uint32 contentSize=0)
Blocking HTTP(S) exchange from a URL string (GET, or POST when content given).
std::map< uint32, NetworkChannel * > channels
All channels by channel id (owned).
NetworkChannel * createUDPConnection(uint16 port, uint8 protocol, bool isAsync, bool autoreconnect, uint32 channelID, NetworkReceiver *recv, uint64 &conid)
Bind a UDP port for datagram traffic.
bool removeConnection(uint64 conid)
Close a connection and remove it from the manager's maps entirely.
virtual ~NetworkManager()
std::map< uint16, NetworkChannel * > listeners
TCP listeners by port.
std::map< uint16, NetworkChannel * > udpListeners
UDP listeners by port.
bool sendUDPMessage(DataMessage *msg, uint64 destination)
Send a DataMessage as a UDP datagram via the shared output socket.
static bool UnitTestDelayedConnect()
Self-test of non-blocking (delayed) connect handling.
Callback interface for asynchronous delivery of parsed network traffic.
Bookkeeping for one worker thread of a NetworkChannel (per listener or connection).
NetworkThread(NetworkChannel *parent, uint64 id)
uint32 defaultProtocol
Fallback PROTOCOL_* when auto-detection is inconclusive.
NetworkChannel * parent
Owning channel (not owned).
TCPListener * listener
Listener served (owned), or NULL for connections.
uint16 port
Local port (listeners) or 0.
uint32 autoProtocolTimeout
Milliseconds allowed for protocol sniffing.
NetworkConnection * con
Connection served (owned), or NULL for listeners.
bool isAsync
Push to receiver (true) or queue for waitFor*() (false).
uint64 id
Connection or listener id served by this thread.
bool autoreconnect
Re-establish the connection automatically on failure.
uint32 threadID
ThreadManager id of the worker thread.
bool isRunning
True while the worker loop is active.
uint32 autoProtocols
PROTOCOL_* bit set to auto-detect among.
bool shouldContinue
Loop control flag: thread exits when false.
HTTPRequest * lastRequest
Last request pending a reply on this HTTP connection.
SSL/TLS-encrypted TCP connection (OpenSSL) with configurable peer verification.
bool init()
Initialise the OpenSSL context for a client-side connection.
void setCALocation(const char *caFile, const char *caPath)
bool connect(SOCKET s, uint64 localAddr, NetworkDataReceiver *receiver=NULL)
Adopt an already-accepted socket and perform the server-side TLS handshake.
void setAllowSelfSigned(bool allow)
bool delayedConnect(uint64 addr, uint32 timeoutMS, NetworkDataReceiver *receiver)
Begin a non-blocking connect (TLS handshake completes in didConnect()).
Plain TCP stream connection (client-initiated or accepted from a listener).
bool connect(SOCKET s, uint64 localAddr, NetworkDataReceiver *receiver=NULL)
Adopt an already-connected socket (server side, from a TCPListener).
bool delayedConnect(uint64 addr, uint32 timeoutMS, NetworkDataReceiver *receiver)
Begin a non-blocking connect; completion is checked with didConnect().
TCP server socket: binds a port and accepts inbound connections (plain or SSL).
NetworkConnection * acceptConnection(uint32 timeout)
Synchronously wait for and accept one inbound connection.
bool setSSLCertificate(const char *sslCertPath, const char *sslKeyPath)
Set the SSL certificate and private key used for inbound SSL connections.
bool init(uint16 port, uint8 encryption, NetworkConnectionReceiver *receiver=NULL, NetworkDataReceiver *dataReceiver=NULL)
Bind and start listening on a port.
One line of Telnet-style text traffic.
static TelnetLine * ReceiveTelnetLine(NetworkConnection *con, uint32 timeout)
Read one line terminated by CR/LF.
static bool CheckBufferForCompatibility(const char *buffer, uint32 length)
static bool SendTelnetLine(NetworkConnection *con, TelnetLine *line)
Send one line (with its line ending).
static bool CreateThread(THREAD_FUNCTION func, void *args, uint32 &newID, uint32 reqID=0)
Create a new native thread and start it immediately.
static bool IsThreadRunning(uint32 id)
Check whether the thread is still alive at the OS level.
static bool TerminateThread(uint32 id)
Forcibly terminate the thread and release its slot.
static bool Shutdown()
Terminate all managed threads, then destroy the singleton.
UDP datagram connection (bound port for input, or output-only sender).
bool connect(uint16 port, NetworkDataReceiver *receiver=NULL)
Bind a local UDP port for receiving datagrams.
static UnitTestRunner & instance()
Access the singleton (created on first use).
void registerTest(const char *name, UnitTestFunc func, const char *description="", const char *category="", bool inDefaultRun=true)
Register a test with the runner.
One WebSocket frame/message (RFC 6455): parsing, generation and control frames.
const char * getContent(uint64 &size)
Get the decoded (unmasked) payload.
bool isTerminationRequest()
enum cmlabs::WebsocketData::DataType dataType
static WebsocketData * CreateTerminationConfirmation()
bool setData(DataType dataType, bool maskData, const char *data=NULL, uint64 size=0)
Set the payload and build the serialised frame for sending.
Minimal WebSocket echo server used by the unit tests (handles upgrade + echo).
virtual bool receiveHTTPRequest(HTTPRequest *req, NetworkChannel *channel, uint64 conid)
Answer the WebSocket upgrade handshake.
virtual bool receiveWebsocketData(WebsocketData *wsData, NetworkChannel *channel, uint64 conid)
Echo received frames back to the client.
uint64 GetTimeNow()
Return the current absolute time (µs since year 0) according to the TMC.
std::string PrintTimeNowString(bool local=true, bool us=true, bool ms=true)
Format GetTimeNow().
std::string PrintTimeDifString(uint64 t, bool us=true, bool ms=true)
int32 GetTimeAgeMS(uint64 t)
Age of a timestamp relative to now, in milliseconds.
bool SeedRandomValues(uint32 seedvalue=0)
Seed the pseudo-random generator.
bool Sleep(uint32 ms)
Suspend the calling thread.
double RandomValue()
Uniform random double in [0,1).
bool GetLocalIPAddress(uint32 &address)
Get the primary local IPv4 address.
#define GETIPADDRESSQUADPORT(a)
#define GETIPADDRESSPORT(a, p)
std::string StringFormat(const char *format,...)
printf into a std::string.
std::string GetURIFromURL(std::string url)
Extract the URI (path plus query) from a URL.
std::string GetProtocolFromURL(std::string url)
Extract the protocol/scheme from a URL.
std::string GetHostFromURL(std::string url)
Extract the host name (or IP literal) from a URL.
uint16 GetPortFromURL(std::string url)
Extract the port number from a URL.
void fail(const char *fmt,...)
Set an explanatory reason shown on the FAIL line.
void metric(const char *name, double value, const char *unit="", bool higherIsBetter=true)
Record a performance metric.
void detail(const char *fmt,...)
Verbose-only indented diagnostic line (shown only when verbose=1).
void progress(int percent, const char *action)
Report progress with a short description of the current action.
THREAD_RET THREAD_FUNCTION_CALL HTTPServerTest(THREAD_ARG arg)
static struct PsyType CTRL_TEST
void Register_NetworkManager_Tests()
Hierarchical message type identifier — the key used for publish/subscribe matching in Psyclone.
std::vector< std::string > * urls
Notification of a connection lifecycle change (connect, disconnect, buffer state.....
uint64 time
Event timestamp (ms epoch).
uint64 cid
Channel id the event belongs to.
uint8 protocol
PROTOCOL_* of the affected connection.
uint64 conid
Connection id within the channel.
uint8 type
NETWORKEVENT_* event type.