CMSDK 2.0.1
Cross-platform C++ base library and SDK for the Psyclone AIOS platform
Loading...
Searching...
No Matches
RequestClient.cpp
Go to the documentation of this file.
1
7#include "RequestClient.h"
8#include "UnitTestFramework.h"
9
10namespace cmlabs{
11
12
14// Request Connection
16
18 uint32 count = longReqQueue.removeStaleRequests(ttlMS);
19 count += shortReqQueue.removeStaleRequests(ttlMS);
20 return count;
21}
22
23
25// Request Reply
27
29 switch (this->status) {
30 case SUCCESS:
31 case FAILED:
32 case TIMEOUT:
33 case TOOBUSY:
34 case LOCALERROR:
35 case NETWORKERROR:
36 case SERVERERROR:
37 return true;
38 default:
39 return false;
40 }
41 return false;
42}
43
45 if (!mutex.enter(1000))
46 return false;
47 this->status = status;
48 mutex.leave();
49 return true;
50}
51
54 if (!mutex.enter(1000))
55 return res;
56 res = this->status;
57 mutex.leave();
58 return res;
59}
60
61
64 if (!mutex.enter(1000))
65 return "Locking error";
66 res = this->status;
67 mutex.leave();
68 switch (res) {
69 case IDLE:
70 return "Request idle";
71 case QUEUED:
72 return "Request queued";
73 case SENT:
74 return "Request sent";
75 case SUCCESS:
76 return "Request success";
77 case FAILED:
78 return "Request failed";
79 case TIMEOUT:
80 return "Request timed out";
81 case LOCALERROR:
82 return "Request local error";
83 case NETWORKERROR:
84 return "Request network error";
85 case SERVERERROR:
86 return "Request server error";
87 case TOOBUSY:
88 return "Request server too busy";
89 default:
90 return "Error getting status";
91 }
92}
93
95 RequestCallbackFunction res = NULL;
96 if (!mutex.enter(1000))
97 return res;
98 res = this->callback;
99 mutex.leave();
100 return res;
101}
102
104 if (!mutex.enter(1000))
105 return false;
106 this->callback = callback;
107 mutex.leave();
108 return true;
109}
110
112 if (!mutex.enter(1000))
113 return false;
114 if (requestMsg)
115 delete(requestMsg);
116 requestMsg = msg;
117 systemID = msg->getSystemID();
118 mutex.leave();
119 return true;
120}
121
123 if (!mutex.enter(1000))
124 return false;
125 if (requestMsg)
126 delete(requestMsg);
127 if (msg)
128 requestMsg = new DataMessage(*msg);
129 else
130 requestMsg = NULL;
131 systemID = msg->getSystemID();
132 mutex.leave();
133 return true;
134}
135
137 DataMessage* msg = NULL;
138 if (!mutex.enter(1000))
139 return NULL;
140 msg = new DataMessage(*requestMsg);
141 mutex.leave();
142 return msg;
143}
144
148
150 if (!mutex.enter(1000))
151 return false;
152 if (replyMsg)
153 delete(replyMsg);
154 replyMsg = msg;
155 mutex.leave();
156 return true;
157}
158
160 if (!mutex.enter(1000))
161 return false;
162 if (replyMsg)
163 delete(replyMsg);
164 if (msg)
165 replyMsg = new DataMessage(*msg);
166 else
167 replyMsg = NULL;
168 mutex.leave();
169 return true;
170}
171
173 DataMessage* msg = NULL;
174 if (!mutex.enter(1000))
175 return NULL;
176 msg = new DataMessage(*replyMsg);
177 mutex.leave();
178 return msg;
179}
180
184
186 //if (!msg) return false;
187 if (!mutex.enter(1000))
188 return false;
189 if (replyMsg)
190 delete(replyMsg);
191 replyMsg = msg;
192 this->status = status;
194 semaphore.signal();
195
196 //if (customRef == 2) {
197 // printf("--- [%u] --- Long request %.3fms \n", msg->getFrom(), ((double)finishTime-startTime)/1000.0);
198 // fflush(stdout);
199 //}
200 //else if (customRef == 1) {
201 // printf("--- [%u] --- Short request %.3fms \n", msg->getFrom(), ((double)finishTime-startTime)/1000.0);
202 // fflush(stdout);
203 //}
204
205 mutex.leave();
206 return true;
207}
208
210 if (finishTime)
211 return finishTime-startTime;
212 else
213 return 0;
214}
215
217 if (finishTime)
218 return (uint32)((finishTime-startTime)/1000);
219 else
220 return 0;
221}
222
224 RequestStatus result;
225 if (!mutex.enter(1000, __FUNCTION__))
226 return LOCALERROR;
227
228 //if ( (status == SUCCESS) || (status == FAILED) || (status == TIMEOUT) || (status == LOCALERROR) || (status == NETWORKERROR) || (status == SERVERERROR) ) {
229 if (isComplete()) {
230 result = status;
231 mutex.leave();
232 return result;
233 }
234
235 mutex.leave();
236 if (semaphore.wait(timeoutMS)) {
237 if (!mutex.enter(1000, __FUNCTION__))
238 return LOCALERROR;
239 //if ( (status == SUCCESS) || (status == FAILED) || (status == TIMEOUT) || (status == LOCALERROR) || (status == NETWORKERROR) || (status == SERVERERROR) ) {
240 if (isComplete()) {
241 result = status;
242 mutex.leave();
243 return result;
244 }
245 mutex.leave();
246 }
247 return TIMEOUT;
248}
249
250DataMessage* RequestReply::waitForMessage(uint32 timeoutMS, bool takeMessage) {
251 // Fast path: if the reply (or a terminal status) is already in, return it
252 // under the mutex. Otherwise block on the semaphore that replyToRequest()
253 // signals, then re-check under the mutex — the double-check is needed
254 // because the semaphore can also be signalled for failure statuses where
255 // replyMsg stays NULL. With takeMessage the internal pointer is cleared so
256 // ownership moves to the caller (and finishRequest() won't double-delete).
257 DataMessage* msg;
258 if (!mutex.enter(1000, __FUNCTION__))
259 return NULL;
260
261 if (replyMsg || finishTime) {
262 msg = replyMsg;
263 if (takeMessage)
264 replyMsg = NULL;
265 mutex.leave();
266 return msg;
267 }
268
269 mutex.leave();
270 if (semaphore.wait(timeoutMS)) {
271 if (!mutex.enter(1000, __FUNCTION__))
272 return NULL;
273 if (replyMsg) {
274 msg = replyMsg;
275 if (takeMessage)
276 replyMsg = NULL;
277 mutex.leave();
278 return msg;
279 }
280 mutex.leave();
281 }
282 return NULL;
283}
284
285
287// Request Client
289
291 lastRefID = 0;
292 sentCount = 0;
293 receivedCount = 0;
294 shouldContinue = true;
295 isRunning = false;
296 channel = NULL;
297 manager = new NetworkManager();
298
300 shouldContinue = false;
301 }
302}
303
305 conMutex.enter(3000, __FUNCTION__);
306 stop();
307
308 std::map<uint64, RequestReply*>::iterator i, e;
309 mutex.enter(1000, __FUNCTION__);
310 for (i = requestMap.begin(), e = requestMap.end(); i != e; i++)
311 delete(i->second);
312 requestMap.clear();
313 mutex.leave();
314 conMutex.leave();
315
316 delete(manager);
317}
318
319bool RequestClient::addGateway(uint32 id, std::string addr, uint16 port, uint8 encryption) {
320
321 std::list<RequestGatewayConnection>::iterator i, e = connections.end();
322 if (!mutex.enter(1000, __FUNCTION__))
323 return false;
324 // Check to see if this gateway is already in the list
325 i = connections.begin();
326 while (i != e) {
327 if ( (stricmp((*i).addr.c_str(), addr.c_str()) == 0) && ((*i).port == port) ) {
328 mutex.leave();
329 return true;
330 }
331 i++;
332 }
333
335 con.clear();
336 con.id = id;
337 con.addr = addr;
338 con.port = port;
339 con.encryption = encryption;
340
341// if (!mutex.enter(1000, __FUNCTION__))
342// return false;
343
344 DataMessage* msgConnect = new DataMessage();
345 msgConnect->setString("URI", "ClientConnect");
346
347 if (!channel)
348 channel = manager->addTCPConnection(con.addr.c_str(), con.port, con.encryption, PROTOCOL_MESSAGE, true, 0, this, con.conID, con.location, 1000, (const char*)msgConnect->data, msgConnect->getSize());
349 else
350 con.conID = channel->addTCPConnection(con.addr.c_str(), con.port, con.encryption, PROTOCOL_MESSAGE, true, con.location, 1000, (const char*)msgConnect->data, msgConnect->getSize());
351
352 delete msgConnect;
353
354 connections.push_back(con);
355 mutex.leave();
356 return true;
357}
358
360 // Correlation scheme: each outgoing request gets the next monotonically
361 // increasing clientRef, stamped into the message's reference field. The
362 // gateway echoes it back in the reply, and receiveMessage() uses it to
363 // find this RequestReply in requestMap. The message itself is owned by
364 // the reply object from here on; the worker thread (run()) picks the
365 // reply up from outQ and dispatches it to a connected gateway.
366 if (!msg)
367 return NULL;
368 if (!mutex.enter(1000, __FUNCTION__))
369 return NULL;
370 msg->setReference(++lastRefID);
371
372 RequestReply* reply = new RequestReply();
373 reply->setStatus(QUEUED);
374 reply->giveRequestMessage(msg);
375 reply->clientRef = lastRefID;
376 requestMap[lastRefID] = reply;
377 outQ.add(reply);
378 mutex.leave();
379 return reply;
380}
381
382bool RequestClient::postRequest(DataMessage *msg, RequestCallbackFunction callback, uint32 timeoutMS) {
383 // ##############
384 return false;
385}
386
388 // Deletes the RequestReply found in requestMap — and with it both the
389 // request and (unless taken via waitForMessage(..., true)) the reply
390 // DataMessage. Despite the isInUse pooling flag, objects are currently
391 // deleted outright rather than recycled (see the commented-out branch).
392 if (!reply || !reply->clientRef) return false;
393 if (!mutex.enter(1000, __FUNCTION__))
394 return false;
395
396 std::map<uint64, RequestReply*>::iterator i = requestMap.find(reply->clientRef);
397 if (i == requestMap.end())
398 return false;
399 if (i->second) {
400 delete i->second;
401 requestMap.erase(i);
402 //if (i->second->isComplete()) {
403 // delete i->second;
404 // requestMap.erase(i);
405 //}
406 //else
407 // i->second->isInUse = false;
408 }
409 mutex.leave();
410 return true;
411}
412
413
415 uint64 now = GetTimeNow();
416 std::list<RequestGatewayConnection>::iterator i, e = connections.end();
417 if (!mutex.enter(1000, __FUNCTION__))
418 return false;
419 i = connections.begin();
420 while (i != e) {
421 if ((*i).conID == conid) {
422 switch (evt->type) {
425 LogPrint(0, LOG_SYSTEM, 0, "Disconnected from gateway %u on %s:%u", conid, (*i).addr.c_str(), (*i).port);
426 (*i).lastFailTime = now;
427 (*i).lastConTime = 0;
428 //(*i).conID = 0;
429 break;
432 LogPrint(0, LOG_SYSTEM, 0, "Connected to gateway %u on %s:%u", conid, (*i).addr.c_str(), (*i).port);
433 (*i).lastFailTime = 0;
434 (*i).lastConTime = now;
435 break;
436 default:
437 break;
438 }
439 }
440 i++;
441 }
442 delete evt;
443 mutex.leave();
444 return true;
445}
446
448 // Network-thread reply path: look up the pending RequestReply by the
449 // echoed reference id and complete it via replyToRequest(), which takes
450 // ownership of msg, wakes any waitForResult()/waitForMessage() waiter and
451 // fires the callback. The terminal status comes from the message's own
452 // status field (set by the gateway: SUCCESS, TIMEOUT, TOOBUSY, ...).
453 // Unmatched replies (already finished/timed out locally) are dropped.
454 if (!msg) return false;
455 if (!mutex.enter(1000, __FUNCTION__)) {
456 LogPrint(0, LOG_SYSTEM, 0, "Error locking Client mutex");
457 delete(msg);
458 return true;
459 }
460 uint64 ref = msg->getReference();
461 std::map<uint64,RequestReply*>::iterator i = requestMap.find(ref);
462
463 if (i == requestMap.end()) {
464 // #############
465 LogPrint(0, LOG_SYSTEM, 0, "Couldn't find reply ID %llu", ref);
466 mutex.leave();
467 delete(msg);
468 return true;
469 }
470 RequestReply* reply = i->second;
471 //reply->replyToRequest(msg, SUCCESS);
472 reply->replyToRequest(msg, (RequestStatus)msg->getStatus());
473 if (!reply->isInUse) {
474 LogPrint(0, LOG_SYSTEM, 0, "Reply ID %llu no longer in use", ref);
475 requestMap.erase(i);
476 delete reply;
477 }
478 //else
479 // LogPrint(0, LOG_SYSTEM, 0, "Got reply ID %llu - status %u", ref, msg->getStatus());
481 mutex.leave();
482 return true;
483}
484
486 return false;
487}
488
489
491
492 uint64 now = GetTimeNow();
493 if (!channel || !con.lastConTime)
494 return false;
495 LogPrint(0, LOG_NETWORK, 3, "Sending data to gateway %u on %s:%u...", con.id, con.addr.c_str(), con.port);
496
497 if (!channel->sendMessage(msg, con.conID)) {
498 if (!con.lastFailTime)
499 LogPrint(0, LOG_SYSTEM, 0, "Unable to send data to gateway %u on %s:%u, disconnecting...", con.id, con.addr.c_str(), con.port);
500 con.lastFailTime = GetTimeNow();
501 // clearly not working, disconnect and try again later
502 //channel->endConnection(con.conID);
503 //con.conID = 0;
504 return false;
505 }
506 return true;
507
508 //if (!con.conID || !channel) {
509 // if (!channel)
510 // channel = manager->createTCPConnection(con.addr.c_str(), con.port, con.encryption, PROTOCOL_MESSAGE, true, true, con.id, this, con.conID, con.location);
511 // else
512 // con.conID = channel->createTCPConnection(con.addr.c_str(), con.port, con.encryption, PROTOCOL_MESSAGE, true, true, con.location);
513 // if (!channel || !con.conID) {
514 // con.lastFailTime = GetTimeNow();
515 // return false;
516 // }
517 // DataMessage* msg = new DataMessage();
518 // msg->setString("URI", "ClientConnect");
519 // if (!channel->sendMessage(msg, con.conID)) {
520 // // clearly not working, disconnect and try again later
521 // channel->endConnection(con.conID);
522 // con.conID = 0;
523 // con.lastFailTime = GetTimeNow();
524 // return false;
525 // }
526 // con.lastConTime = GetTimeNow();
527 //}
528 //if (!channel->sendMessage(msg, con.conID)) {
529 // // clearly not working, disconnect and try again later
530 // channel->endConnection(con.conID);
531 // con.conID = 0;
532 // con.lastFailTime = GetTimeNow();
533 // return false;
534 //}
535 //con.lastFailTime = 0;
536 //return true;
537}
538
539
541 // Worker loop: (1) every ~3s push a ClientStatus heartbeat to all gateways
542 // (also doubles as a liveness probe that triggers reconnection inside
543 // sendMessageToGateway); (2) drain outQ with a 50ms blocking wait and hand
544 // each pending RequestReply to sendRequest(), which picks a connected
545 // gateway. Reply matching happens on the network thread in
546 // receiveMessage(), not here.
547 RequestReply* reply;
548 isRunning = true;
549
550 // should regularly test for old replies to be deleted...
551 // ###############
552
553 int32 heartbeatInterval = 3000;
554 uint64 lastHeartbeat = 0;
555
556 while (shouldContinue) {
557 if (!lastHeartbeat || GetTimeAgeMS(lastHeartbeat) > heartbeatInterval) {
559 lastHeartbeat = GetTimeNow();
560 }
561
564 //if (!channel && connections.size()) {
565 // conMutex.enter(3000, __FUNCTION__);
566 // DataMessage* msg = new DataMessage();
567 // msg->setString("URI", "ClientStatus");
568 // std::list<RequestGatewayConnection>::iterator iCon, eCon = connections.end();
569 // iCon = connections.begin();
570 // while (iCon != eCon) {
571 // if (sendMessageToGateway(msg, (*iCon)))
572 // break;
573 // iCon++;
574 // }
575 // delete msg;
576 // conMutex.leave();
577 //}
578
579 if (reply = outQ.waitForAndTakeFirst(50))
580 sendRequest(reply);
581
583 }
584 isRunning = false;
585 return true;
586}
587
589
590 if (!conMutex.enter(1000, __FUNCTION__)) {
591 LogPrint(0, LOG_SYSTEM, 0, "Mutex error sending heartbeat from Client...");
592 return 0;
593 }
594 if (!connections.size()) {
595 //LogPrint(0, LOG_SYSTEM, 0, "No connections sending heartbeat from Executor %u...", executorID);
596 conMutex.leave();
597 return 0;
598 }
599
600 DataMessage* msg = new DataMessage();
601 msg->setString("URI", "ClientStatus");
602
603 uint32 success = 0, failed = 0, notConnected = 0;
604
605 std::list<RequestGatewayConnection>::iterator iCon, eCon = connections.end();
606 iCon = connections.begin();
607 while (iCon != eCon) {
608 if (sendMessageToGateway(msg, (*iCon)))
609 success++;
610 else
611 failed++;
612 iCon++;
613 }
614 conMutex.leave();
615
616 delete(msg);
617 if (failed)
618 LogPrint(0, LOG_SYSTEM, 2, "Failed sending heartbeat from Client to %u gateways (%u succeeded)...", failed, success);
619 // else
620 // LogPrint(0, LOG_SYSTEM, 0, "Sent heartbeat from Executor %u to %u gateways...", executorID, success);
621 return success;
622}
623
624
625
626bool RequestClient::waitForConnection(uint32 timeoutMS) {
627 bool isCon = false;
628 uint64 start = GetTimeNow();
629 while (!(isCon = isConnected()) && (GetTimeAgeMS(start) < (int32)timeoutMS))
630 utils::Sleep(50);
631 return isCon;
632}
633
635 if (!conMutex.enter(3000, __FUNCTION__))
636 return false;
637 std::list<RequestGatewayConnection>::iterator iCon, eCon = connections.end();
638 iCon = connections.begin();
639 while (iCon != eCon) {
640 //printf("RUN0\n");fflush(stdout);
641 //if ((*iCon).conID && channel->isConnected((*iCon).conID)) {
642 if ((*iCon).conID && (*iCon).lastConTime) {
643 conMutex.leave();
644 return true;
645 }
646 iCon++;
647 }
648 conMutex.leave();
649 return false;
650 //bool res = false;
652 //DataMessage* msg = new DataMessage();
653 //msg->setString("URI", "ClientStatus");
654 //iCon = connections.begin();
655 //while (iCon != eCon) {
656 // if (sendMessageToGateway(msg, (*iCon)))
657 // break;
658 // iCon++;
659 //}
660 //conMutex.leave();
661 //return res;
662}
663
665 if (!conMutex.enter(3000, __FUNCTION__))
666 return false;
667 std::list<RequestGatewayConnection>::iterator iCon, eCon = connections.end();
668 iCon = connections.begin();
669 while (iCon != eCon) {
670 if ((*iCon).conID && channel->isConnected((*iCon).conID)) {
671 channel->endConnection((*iCon).conID);
672 }
673 iCon++;
674 }
675 conMutex.leave();
676 return true;
677}
678
679
681 if (!reply)
682 return false;
683
684 uint64 now = GetTimeNow();
685 conMutex.enter(3000, __FUNCTION__);
686 std::list<RequestGatewayConnection>::iterator iCon, eCon = connections.end();
687 iCon = connections.begin();
688 while (iCon != eCon) {
689 if (sendMessageToGateway(reply->peekRequestMessage(), (*iCon))) {
690 reply->setStatus(SENT);
691 sentCount++;
692 conMutex.leave();
693 return true;
694 }
695 iCon++;
696 }
697 // We didn't manage to send it, mark it as failed
698 reply->replyToRequest(NULL, NETWORKERROR);
699 //reply->setStatus(NETWORKERROR);
700 conMutex.leave();
701 return false;
702}
703
705 if (arg == NULL) thread_ret_val(1);
706 thread_ret_val((int)(((RequestClient*)arg)->run() ? 0 : 1));
707}
708
709
710
711
712
713
714std::string RequestClient::GetJSONReplyParameter(const char* json, const char* name) {
715
716 jsmn_parser parser;
717 jsmntok_t* tokens = new jsmntok_t[1024];
718 int tokenCount = 0;
719 const char* val;
720 int valSize;
721 jsmn_init(&parser);
722 if ((tokenCount = jsmn_parse(&parser, json, strlen(json), tokens, 1024)) <= 0) {
723 // No JSON found
724 delete[] tokens;
725 return "";
726 }
727 if (!(val = GetJSONValue(tokens, tokenCount, json, name, JSMN_UNDEFINED, valSize))) {
728 // JSON key not found
729 delete[] tokens;
730 return "";
731 }
732 delete[] tokens;
733 std::string value = std::string(val, valSize);
734 return value.c_str();
735}
736
738 //if (!client->isConnected())
739 // return NULL;
740
741 DataMessage *replyMsg;
742 RequestReply* reply;
743 if (reply = client->postRequest(msg)) {
744 if (replyMsg = reply->waitForMessage(timeoutMS, true)) {
745 client->finishRequest(reply);
746 //delete(reply);
747 return replyMsg;
748 }
749 client->finishRequest(reply);
750 //delete(reply);
751 }
752 return NULL;
753}
754
756 //DataMessage *replyMsg;
757 RequestReply* reply;
758 if (reply = client->postRequest(msg)) {
759 reply->waitForResult(timeoutMS);
760 return reply;
761 //client->finishRequest(reply);
762 }
763 return NULL;
764}
765
766
767std::string RequestClient::SendRequestAndWaitForJSON(RequestClient* client, DataMessage* msg, int timeoutMS) {
768 //if (!client->isConnected())
769 // return "";
770
771 DataMessage *replyMsg = SendRequestAndWaitForReply(client, msg, timeoutMS);
772 if (!replyMsg)
773 return "";
774 const char* json = replyMsg->getString("JSON");
775 if (!json) {
776 delete(replyMsg);
777 return "";
778 }
779
780 std::string jsonReply = json;
781 delete(replyMsg);
782 return jsonReply;
783}
784
785DataMessage* RequestClient::CreateRequestMessage(uint8 operation, const char* req,
786 const char* key1, const char* val1,
787 const char* key2, const char* val2,
788 const char* key3, const char* val3,
789 const char* key4, const char* val4,
790 const char* key5, const char* val5,
791 const char* key6, const char* val6,
792 const char* key7, const char* val7,
793 const char* key8, const char* val8,
794 const char* key9, const char* val9,
795 const char* key10, const char* val10,
796 const char* key11, const char* val11,
797 const char* key12, const char* val12,
798 const char* key13, const char* val13,
799 const char* key14, const char* val14,
800 const char* key15, const char* val15,
801 const char* key16, const char* val16,
802 const char* key17, const char* val17,
803 const char* key18, const char* val18,
804 const char* key19, const char* val19,
805 const char* key20, const char* val20
806 ) {
807 DataMessage* msg = new DataMessage();
808 msg->setInt("HTTP_OPERATION", operation);
809 msg->setString("URI", req);
810 if (key1) msg->setString(key1, val1);
811 if (key2) msg->setString(key2, val2);
812 if (key3) msg->setString(key3, val3);
813 if (key4) msg->setString(key4, val4);
814 if (key5) msg->setString(key5, val5);
815 if (key6) msg->setString(key6, val6);
816 if (key7) msg->setString(key7, val7);
817 if (key8) msg->setString(key8, val8);
818 if (key9) msg->setString(key9, val9);
819 if (key10) msg->setString(key10, val10);
820 if (key11) msg->setString(key11, val11);
821 if (key12) msg->setString(key12, val12);
822 if (key13) msg->setString(key13, val13);
823 if (key14) msg->setString(key14, val14);
824 if (key15) msg->setString(key15, val15);
825 if (key16) msg->setString(key16, val16);
826 if (key17) msg->setString(key17, val17);
827 if (key18) msg->setString(key18, val18);
828 if (key19) msg->setString(key19, val19);
829 if (key20) msg->setString(key20, val20);
830 return msg;
831}
832
833
834
835
836
837bool RequestClient::TestServerLogin(const char* address, uint16 port, const char* username, const char* password, const char* reqAfterLogin) {
838
839 RequestClient* reqClient = new RequestClient();
840 // api->logPrint(1, "Connecting to Face Server on %s:%u...", addr.c_str(), port, username.c_str());
841 if (!reqClient->addGateway(1, address, port, NOENC)) {
842 LogPrint(0, 0, 1, "Could not connect to Face Server on %s:%u...", address, port);
843 delete(reqClient);
844 return false;
845 }
846
847 std::string json;
849 reqClient,
850 RequestClient::CreateRequestMessage(HTTP_POST, "authenticate", "email", username, "password", password, "clientid", "1", "locationid", "1"),
851 5000)).length()) {
852 LogPrint(0, 0, 1, "Couldn't login to server account...");
853 return false;
854 }
855 std::string token = RequestClient::GetJSONReplyParameter(json.c_str(), "token");
856 if (!token.length()) {
857 LogPrint(0, 0, 1, "Client login failed: %s...", RequestClient::GetJSONReplyParameter(json.c_str(), "text").c_str());
858 return false;
859 }
860
861 LogPrint(0, 0, 1, "Successfully logged in with token: %s...", token.c_str());
862
863 if (!reqClient->isConnected()) {
864 LogPrint(0, 0, 1, "Couldn't connect to server...");
865 return false;
866 }
867
868 return true;
869}
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
890 this->id = id;
891 port = 10000;
892 gatewayCount = 1;
893 sendfreq = 1;
894 reconnectMS = 0;
895 payload = 1024;
896 shouldContinue = true;
897 isRunning = true;
898 shouldFinishUp = false;
899}
900
901bool TestRequestClient::init(uint16 port, uint32 gatewayCount, double sendfreq, int32 reconnectMS, uint32 payload, const char* longTestName) {
902 uint32 myThreadID;
903 this->port = port;
904 if (gatewayCount)
905 this->gatewayCount = gatewayCount;
906 else
907 this->gatewayCount = 1;
908 this->sendfreq = sendfreq;
909 this->reconnectMS = reconnectMS;
910 this->payload = payload;
911 this->longTestName = longTestName;
912 return ThreadManager::CreateThread(TestRequestClientRun, this, myThreadID, 0);
913}
914
916 LogPrint(0, LOG_SYSTEM, 0, "Client %u has been asked to finish up...", id);
917 shouldFinishUp = true;
918 return true;
919}
920
922 RequestClient* client = NULL;
923 //DataMessage* replyMsg;
924 RequestReply* reply;
925 std::list<RequestReply*> replyList;
926 std::list<RequestReply*>::iterator i, e = replyList.end();
927 RequestStatus status;
928
929 int32 intervalMS = 1000;
930 if (sendfreq > 0)
931 intervalMS = (int32)(1000.0 / sendfreq);
932
933 uint64 lastPost = 0;
934 uint64 lastReconnect = 0;
935 uint64 lastPrint = GetTimeNow();
936 int32 printIntervalMS = 3000;
937
938 uint32 stillWaiting, failed, timeout, errors, success, total, tooBusy, serverError;
939 stillWaiting = failed = timeout = errors = success = total = tooBusy = serverError = 0;
940
941 char* bigdata = new char[payload];
942 memset(bigdata, 1, payload);
943
944 DataMessage* msgTemplate = new DataMessage();
945 msgTemplate->setString("URI", "longtest");
946 msgTemplate->setData("BigData", bigdata, payload);
947
948 delete[] bigdata;
949
950 Stats responseTimes;
951
952 while (shouldContinue) {
953
954 if (!client) {
955 client = new RequestClient();
956 for (uint16 m = 0; m<gatewayCount; m++) {
957 if (!client->addGateway(m, "localhost", port + m, NOENC)) {
958 LogPrint(0, LOG_SYSTEM, 0, "Couldn't add gateway %u to client %u...\n", m);
959 delete msgTemplate;
960 return false;
961 }
962 }
963 lastReconnect = GetTimeNow();
964 utils::Sleep(100);
965 }
966
967 // is it time to send the next message?
968 if (!shouldFinishUp && (!lastPost || (GetTimeAgeMS(lastPost) >= intervalMS))) {
969
970 if (reconnectMS < 0) {
971 reply = RequestClient::SendRequestAndWaitForReplyObject(client, msgTemplate->copy(), 5000);
972 if (!reply) {
973 LogPrint(0, 0, 1, "Couldn't send request to server...");
974 //delete msgTemplate;
975 //return false;
976 failed++;
977 }
978
979 status = reply->getStatus();
980 switch (status) {
981 case SUCCESS:
982 responseTimes.add((double)reply->getRequestDuration());
983 success++;
984 break;
985 case FAILED:
986 failed++;
987 break;
988 case TOOBUSY:
989 tooBusy++;
990 break;
991 case LOCALERROR:
992 case NETWORKERROR:
993 case SERVERERROR:
994 serverError++;
995 break;
996 case TIMEOUT:
997 timeout++;
998 break;
999 default:
1000 stillWaiting++;
1001 break;
1002 }
1003 client->finishRequest(reply);
1004 delete client;
1005 client = NULL;
1006 lastPost = GetTimeNow();
1007 continue;
1008 }
1009
1010 // post request
1011 reply = client->postRequest(msgTemplate->copy());
1012 if (!reply) {
1013 LogPrint(0, 0, 1, "Couldn't post request to server...");
1014 //delete msgTemplate;
1015 //return false;
1016 }
1017 replyList.push_back(reply);
1018 lastPost = GetTimeNow();
1019 total++;
1020 }
1021
1022 stillWaiting = 0;
1023 i = replyList.begin();
1024 while (i != e) {
1025 reply = *i;
1026 status = reply->getStatus();
1027 switch (status) {
1028 case SUCCESS:
1029 responseTimes.add((double)reply->getRequestDuration());
1030 success++;
1031 break;
1032 case FAILED:
1033 failed++;
1034 break;
1035 case TOOBUSY:
1036 tooBusy++;
1037 break;
1038 case LOCALERROR:
1039 case NETWORKERROR:
1040 case SERVERERROR:
1041 serverError++;
1042 break;
1043 case TIMEOUT:
1044 timeout++;
1045 break;
1046 default:
1047 stillWaiting++;
1048 break;
1049 }
1050 if (reply->isComplete()) {
1051 client->finishRequest(reply);
1052 i = replyList.erase(i);
1053 }
1054 else
1055 i++;
1056
1057 /*
1058 if (!stillWaiting || GetTimeAgeMS(startTime) > 60000)
1059 break;
1060 //if (stillWaiting != total) {
1061 LogPrint(0, LOG_SYSTEM, 0, "----- [%u/%u] success: %u failed: %u timeout: %u errors: %u\n",
1062 stillWaiting, total, success, failed, timeout, errors);
1063 //printf("--- [%u] --- Long request %.3fms \n", reply->peekReplyMessage()->getFrom(), reply->getRequestDuration()/1000.0);
1064 //}
1065 utils::Sleep(200);
1066 */
1067 }
1068
1069 if (shouldFinishUp && !stillWaiting) {
1070 break;
1071 }
1072
1073 if (GetTimeAgeMS(lastPrint) > printIntervalMS) {
1074 if (tooBusy || serverError || failed || timeout || errors)
1075 LogPrint(0, LOG_SYSTEM, 0, "%s[%u] success: %u of %u (w: %u): %.3fms - busy: %u servererror: %u failed: %u timeout: %u errors: %u\n",
1076 shouldFinishUp ? "Finishing " : "",
1077 id, success, total, stillWaiting, responseTimes.getAverage() / 1000.0, tooBusy, serverError, failed, timeout, errors);
1078 else
1079 LogPrint(0, LOG_SYSTEM, 0, "%s[%u] success: %u of %u (w: %u): %.3fms\n",
1080 shouldFinishUp ? "Finishing " : "",
1081 id, success, total, stillWaiting, responseTimes.getAverage() / 1000.0);
1082 lastPrint = GetTimeNow();
1083 }
1084
1085
1086 if ((reconnectMS > 0) && (GetTimeAgeMS(lastReconnect) >= reconnectMS)) {
1087 LogPrint(0, LOG_SYSTEM, 0, "Client %u forcefully reconnecting...");
1088 delete client;
1089 client = NULL;
1090 // delete all unprocessed replies
1091 replyList.clear();
1092 continue;
1093 }
1094
1095 utils::Sleep(20);
1096 }
1097
1098 if (tooBusy || serverError || failed || timeout || errors)
1099 LogPrint(0, LOG_SYSTEM, 0, "Completed [%u] success: %u of %u (w: %u): %.3fms - busy: %u servererror: %u failed: %u timeout: %u errors: %u\n",
1100 id, success, total, stillWaiting, responseTimes.getAverage() / 1000.0, tooBusy, serverError, failed, timeout, errors);
1101 else
1102 LogPrint(0, LOG_SYSTEM, 0, "Completed [%u] success: %u of %u (w: %u): %.3fms\n",
1103 id, success, total, stillWaiting, responseTimes.getAverage() / 1000.0);
1104
1105 delete client;
1106 client = NULL;
1107 isRunning = false;
1108 delete msgTemplate;
1109 return true;
1110}
1111
1113 if (arg == NULL) thread_ret_val(1);
1114 thread_ret_val((int)(((TestRequestClient*)arg)->run() ? 0 : 1));
1115}
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1130 this->id = id;
1131 port = 10000;
1132 gatewayCount = 1;
1133 sendfreq = 1;
1134 reconnectMS = 0;
1135 payload = 1024;
1136 shouldContinue = true;
1137 isRunning = true;
1138 shouldFinishUp = false;
1139}
1140
1141bool TestWebRequestClient::init(uint16 port, uint32 gatewayCount, double sendfreq, int32 reconnectMS, uint32 payload, const char* longTestName) {
1142 uint32 myThreadID;
1143 this->port = port;
1144 if (gatewayCount)
1145 this->gatewayCount = gatewayCount;
1146 else
1147 this->gatewayCount = 1;
1148 this->sendfreq = sendfreq;
1149 this->reconnectMS = reconnectMS;
1150 this->payload = payload;
1151 this->longTestName = longTestName;
1152 return ThreadManager::CreateThread(TestWebRequestClientRun, this, myThreadID, 0);
1153}
1154
1156 LogPrint(0, LOG_SYSTEM, 0, "Client %u has been asked to finish up...", id);
1157 shouldFinishUp = true;
1158 return true;
1159}
1160
1162
1163 int32 intervalMS = 1000;
1164 if (sendfreq > 0)
1165 intervalMS = (int32)(1000.0 / sendfreq);
1166 uint64 lastPost = 0;
1167 uint64 lastPrint = GetTimeNow();
1168 int32 printIntervalMS = 3000;
1169
1170 uint32 stillWaiting, failed, timeout, errors, success, total, tooBusy, serverError;
1171 stillWaiting = failed = timeout = errors = success = total = tooBusy = serverError = 0;
1172
1173 NetworkManager* manager = new NetworkManager();
1174
1175 int8 encryption = NOENC;
1176
1177 char* bigdata = new char[payload];
1178 memset(bigdata, 1, payload);
1179
1180 std::string uriString = utils::StringFormat("/api/longtest");
1181
1182 HTTPRequest* reqTemplate = new HTTPRequest();
1183 reqTemplate->createRequest(HTTP_POST, "", uriString.c_str(), bigdata, payload, false, 0);
1184 delete[] bigdata;
1185
1186 HTTPRequest* req;
1187 HTTPReply* reply;
1188
1189 while (shouldContinue) {
1190 // is it time to send the next message?
1191 if (!shouldFinishUp && (!lastPost || (GetTimeAgeMS(lastPost) >= intervalMS))) {
1192 req = new HTTPRequest(reqTemplate);
1193 reply = manager->makeHTTPRequest(req, "localhost", port, encryption, 5000);
1194 delete(req);
1195 if (!reply) {
1196 LogPrint(0, LOG_SYSTEM, 0, "WebClient %u got no reply to HTTP request", id);
1197 serverError++;
1198 }
1199 else {
1200 //LogPrint(0, LOG_SYSTEM, 0, "WebClient %u got a reply to HTTP request: %s", id, HTTP_Status[reply->type]);
1201 switch (reply->type) {
1202 case HTTP_OK:
1204 success++;
1205 break;
1207 case HTTP_MALFORMED_URL:
1208 case HTTP_UNAUTHORIZED:
1209 case HTTP_ACCESS_DENIED:
1214 errors++;
1215 break;
1217 timeout++;
1218 break;
1222 default:
1223 serverError++;
1224 break;
1225 }
1226 }
1227 delete(reply);
1228 lastPost = GetTimeNow();
1229 total++;
1230 }
1231
1232 if (shouldFinishUp) {
1233 LogPrint(0, LOG_SYSTEM, 0, "[%u] finished total: %u success: %u busy: %u servererror: %u failed: %u timeout: %u errors: %u\n",
1234 id, total, success, tooBusy, serverError, failed, timeout, errors);
1235 break;
1236 }
1237
1238 if (GetTimeAgeMS(lastPrint) > printIntervalMS) {
1239 if (shouldFinishUp)
1240 LogPrint(0, LOG_SYSTEM, 0, "Client %u finishing up... (%u/%u) success: %u busy: %u servererror: %u failed: %u timeout: %u errors: %u\n",
1241 id, stillWaiting, total, success, tooBusy, serverError, failed, timeout, errors);
1242 else
1243 LogPrint(0, LOG_SYSTEM, 0, "[%u] (%u/%u) success: %u busy: %u servererror: %u failed: %u timeout: %u errors: %u\n",
1244 id, stillWaiting, total, success, tooBusy, serverError, failed, timeout, errors);
1245 lastPrint = GetTimeNow();
1246 }
1247
1248 utils::Sleep(20);
1249 }
1250
1251 delete manager;
1252 isRunning = false;
1253 return true;
1254}
1255
1257 if (arg == NULL) thread_ret_val(1);
1258 thread_ret_val((int)(((TestWebRequestClient*)arg)->run() ? 0 : 1));
1259}
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1274 this->id = id;
1275 port = 10000;
1276 gatewayCount = 1;
1277 sendfreq = 1;
1278 reconnectMS = 0;
1279 payload = 1024;
1280 shouldContinue = true;
1281 isRunning = true;
1282 shouldFinishUp = false;
1283}
1284
1285bool TestWebSocketRequestClient::init(uint16 port, uint32 gatewayCount, double sendfreq, int32 reconnectMS, uint32 payload, const char* longTestName) {
1286 uint32 myThreadID;
1287 this->port = port;
1288 if (gatewayCount)
1289 this->gatewayCount = gatewayCount;
1290 else
1291 this->gatewayCount = 1;
1292 this->sendfreq = sendfreq;
1293 this->reconnectMS = reconnectMS;
1294 this->payload = payload;
1295 this->longTestName = longTestName;
1297}
1298
1300 LogPrint(0, LOG_SYSTEM, 0, "Client %u has been asked to finish up...", id);
1301 shouldFinishUp = true;
1302 return true;
1303}
1304
1306
1307 int32 intervalMS = 1000;
1308 if (sendfreq > 0)
1309 intervalMS = (int32)(1000.0 / sendfreq);
1310 uint64 lastPost = 0;
1311 uint64 lastPrint = GetTimeNow();
1312 int32 printIntervalMS = 3000;
1313
1314 uint32 stillWaiting, failed, timeout, errors, success, total, tooBusy, serverError;
1315 stillWaiting = failed = timeout = errors = success = total = tooBusy = serverError = 0;
1316
1317 NetworkManager* manager = new NetworkManager();
1318
1319 int8 encryption = NOENC;
1320 //payload = 40;
1321 char* bigdata = new char[payload];
1322 memset(bigdata, 1, payload);
1323
1324 std::string uriString = utils::StringFormat("/longtest");
1325
1326 WebsocketData* wsDataReply;
1327 JSONM* jmReply;
1328 WebsocketData* wsData = new WebsocketData();
1329 JSONM* jm = new JSONM();
1330// jm->setJSON("{\"requestid\": 1}");
1331 jm->addData("Image1", bigdata, payload, "raw");
1332 //wsData->maskingKey = 2112143814;
1333 //wsData->setData(wsData->BINARY, true, jm->jmData, jm->getSize());
1334 //reqTemplate->createRequest(HTTP_POST, "", uriString.c_str(), bigdata, payload, false, 0);
1335 delete[] bigdata;
1336 //delete jm;
1337
1338 NetworkChannel* channel = NULL;
1339 uint64 conID = 0;
1340 uint64 c = 0;
1341
1342 while (shouldContinue) {
1343 // is it time to send the next message?
1344 if (!shouldFinishUp && (!lastPost || (GetTimeAgeMS(lastPost) >= intervalMS))) {
1345
1346 std::string url = utils::StringFormat("http://localhost:%u/", port);
1347
1348 if (!channel)
1349 channel = manager->createWebsocketConnection(url.c_str(), 0, NULL, conID, "JSONM");
1350 else if (!conID)
1351 conID = channel->createWebsocketConnection(url.c_str(), "JSONM");
1352
1353 if (!conID) {
1354 LogPrint(0, 0, 1, "Couldn't connect to Websocket server on: %s", url.c_str());
1355 lastPost = GetTimeNow();
1356 continue;
1357 }
1358
1359 jm->setJSON(utils::StringFormat("{\"requestid\": %llu, \"uri\": \"%s\", \"operation\": \"get\"}",
1360 ++c, uriString.c_str()).c_str());
1361 wsData->setData(wsData->BINARY, true, jm->jmData, jm->getSize());
1362
1363 //req = new HTTPRequest(reqTemplate);
1364 if (!channel->sendWebsocketData(wsData, conID)) {
1365 channel->endConnection(conID);
1366 LogPrint(0, 0, 1, "Lost connection to server...");
1367 lastPost = GetTimeNow();
1368 continue;
1369 }
1370 lastPost = GetTimeNow();
1371 total++;
1372 }
1373
1374 if (conID) {
1375 if (wsDataReply = channel->waitForWebsocketData(conID, 20)) {
1376 jmReply = new JSONM(wsDataReply->data, wsDataReply->getPayloadSize());
1377 if (jmReply->isValid()) {
1378 success++;
1379 }
1380 else {
1381 failed++;
1382 }
1383 delete jmReply;
1384 delete wsDataReply;
1385 }
1386 }
1387 else
1388 utils::Sleep(20);
1389
1390 if (shouldFinishUp) {
1391 LogPrint(0, LOG_SYSTEM, 0, "[%u] finished total: %u success: %u busy: %u servererror: %u failed: %u timeout: %u errors: %u\n",
1392 id, total, success, tooBusy, serverError, failed, timeout, errors);
1393 break;
1394 }
1395
1396 if (GetTimeAgeMS(lastPrint) > printIntervalMS) {
1397 if (shouldFinishUp)
1398 LogPrint(0, LOG_SYSTEM, 0, "Client %u finishing up... (%u/%u) success: %u busy: %u servererror: %u failed: %u timeout: %u errors: %u\n",
1399 id, stillWaiting, total, success, tooBusy, serverError, failed, timeout, errors);
1400 else
1401 LogPrint(0, LOG_SYSTEM, 0, "[%u] (%u/%u) success: %u busy: %u servererror: %u failed: %u timeout: %u errors: %u\n",
1402 id, stillWaiting, total, success, tooBusy, serverError, failed, timeout, errors);
1403 lastPrint = GetTimeNow();
1404 }
1405
1406 //utils::Sleep(20);
1407 }
1408
1409 delete jm;
1410 delete wsData;
1411 delete manager;
1412 isRunning = false;
1413 return true;
1414}
1415
1420
1421
1423// Unit Test
1425//
1426// Self-contained: stands up a local PROTOCOL_MESSAGE listener (the "gateway"
1427// server) on a high localhost port, points a RequestClient at it, exchanges a
1428// bounded set of request/reply messages, then shuts everything down. No
1429// external server, no infinite waits.
1430//
1431// The server side is driven from this same thread using the listener channel's
1432// polling API (waitForMessage / sendMessage): for every request the client
1433// posts, the server receives it, stamps it SUCCESS and echoes it straight back
1434// on the same connection. The client matches the reply to its outstanding
1435// request by reference, exactly as it would against a real gateway.
1436
1437static bool Test_RequestClient() {
1438 const uint16 port = 38050;
1439 const uint32 reqCount = 20;
1440 const uint32 payload = 1024;
1441
1442 unittest::progress(0, "starting server");
1443
1444 NetworkManager* server = new NetworkManager();
1445 NetworkChannel* listen = server->createListener(port, NOENC, PROTOCOL_MESSAGE, true, 0, true, 0, NULL);
1446 if (!listen) {
1447 unittest::fail("could not start message listener on localhost:%u", port);
1448 delete server;
1449 return false;
1450 }
1451
1452 unittest::progress(15, "connecting client");
1453
1454 RequestClient* client = new RequestClient();
1455 if (!client->addGateway(1, "localhost", port, NOENC)) {
1456 unittest::fail("could not add gateway localhost:%u to client", port);
1457 delete client;
1458 delete server;
1459 return false;
1460 }
1461
1462 // The client's addGateway sends a "ClientConnect" greeting on connect; the
1463 // server must drain it (it carries no request reference).
1464 uint64 recvid = 0;
1465 DataMessage* greeting = listen->waitForMessage(recvid, 3000);
1466 if (!greeting) {
1467 unittest::fail("server never received the client connect greeting");
1468 delete client;
1469 delete server;
1470 return false;
1471 }
1472 unittest::detail("received client greeting URI='%s' on conid %llu",
1473 greeting->getString("URI") ? greeting->getString("URI") : "", recvid);
1474 delete greeting;
1475
1476 // The client only forwards requests once its connect event has landed
1477 // (lastConTime set); wait for that so the first postRequest is not dropped.
1478 if (!client->waitForConnection(3000)) {
1479 unittest::fail("client did not register as connected to localhost:%u", port);
1480 delete client;
1481 delete server;
1482 return false;
1483 }
1484
1485 unittest::progress(40, "exchanging requests");
1486
1487 char* dat = new char[payload];
1488 memset(dat, 7, payload);
1489
1490 uint32 ok = 0;
1491 uint64 t0 = GetTimeNow();
1492 for (uint32 n = 0; n < reqCount; n++) {
1493 // Build and post a request; postRequest takes ownership of the message
1494 // and stamps it with a unique reference.
1495 DataMessage* req = new DataMessage();
1496 req->setString("URI", "unittest");
1497 req->setData("BigData", dat, payload);
1498 RequestReply* reply = client->postRequest(req);
1499 if (!reply) {
1500 unittest::fail("postRequest returned NULL on iteration %u", n);
1501 break;
1502 }
1503
1504 // Server side: receive the forwarded request and echo it back as a
1505 // successful reply on the same connection (reference is preserved).
1506 uint64 conid = 0;
1507 DataMessage* got = listen->waitForMessage(conid, 3000);
1508 if (!got) {
1509 unittest::fail("server did not receive request %u", n);
1510 break;
1511 }
1512 got->setStatus(SUCCESS);
1513 if (!listen->sendMessage(got, conid)) {
1514 unittest::fail("server failed to send reply for request %u", n);
1515 delete got;
1516 break;
1517 }
1518 // sendMessage does not take ownership here.
1519 delete got;
1520
1521 // Client side: wait for the matched reply.
1522 DataMessage* replyMsg = reply->waitForMessage(3000, true);
1523 if (!replyMsg) {
1524 unittest::fail("client got no reply for request %u", n);
1525 client->finishRequest(reply);
1526 break;
1527 }
1528 if (replyMsg->getStatus() != SUCCESS) {
1529 unittest::fail("reply %u had unexpected status %u", n, replyMsg->getStatus());
1530 delete replyMsg;
1531 client->finishRequest(reply);
1532 break;
1533 }
1534 delete replyMsg;
1535 client->finishRequest(reply);
1536 ok++;
1537 unittest::detail("request %u round-tripped ok", n);
1538 unittest::progress(40 + (n * 50) / reqCount, "exchanging requests");
1539 }
1540 double us = (double)(GetTimeNow() - t0);
1541
1542 delete[] dat;
1543
1544 unittest::progress(95, "shutting down");
1545 delete client;
1546 delete server;
1547
1548 if (ok != reqCount)
1549 return false;
1550
1551 if (us > 0)
1552 unittest::metric("request_throughput", (double)reqCount / us * 1e6, "req/s", true);
1553 unittest::metric("avg_request_latency", us / reqCount, "us", false);
1554
1555 unittest::progress(100, "done");
1556 return true;
1557}
1558
1561 "requestclient", Test_RequestClient,
1562 "Request client/gateway round-trip over localhost", "network");
1563}
1564
1565}
#define NOENC
Plain, unencrypted transport.
#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 HTTP_OK
200 OK
#define HTTP_SERVICE_NOT_AVAILABLE
503 Service Unavailable
#define HTTP_MALFORMED_URL
400 Bad Request
#define HTTP_GATEWAY_TIMEOUT
504 Gateway Timeout
#define HTTP_SERVER_MALFORMED_REPLY
500 (malformed backend reply)
#define HTTP_INTERNAL_ERROR
500 Internal Server Error (page generation)
#define HTTP_MOVED_PERMANENTLY
301 Moved Permanently
#define HTTP_USE_LOCAL_COPY
304 Not Modified (use cached copy)
#define HTTP_ACCESS_DENIED
403 Forbidden
#define HTTP_FILE_NOT_FOUND
404 Not Found
#define HTTP_UNAUTHORIZED
401 Unauthorized (triggers Basic auth)
#define HTTP_SERVER_NOREPLY
500 (no reply from backend server)
#define HTTP_POST
POST.
#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)
Request-system client side: RequestReply futures, RequestQueue, gateway connection records and the Re...
Small, dependency-free unit test harness used by all CMSDK object tests.
#define LOG_SYSTEM
Definition Utils.h:197
#define thread_ret_val(ret)
Definition Utils.h:131
#define THREAD_RET
Definition Utils.h:127
#define stricmp
Definition Utils.h:132
#define THREAD_FUNCTION_CALL
Definition Utils.h:129
#define LogPrint
Definition Utils.h:313
#define LOG_NETWORK
Definition Utils.h:198
#define THREAD_ARG
Definition Utils.h:130
The central Psyclone data container: a self-contained binary message with typed, named user entries.
uint32 getSystemID()
getSystemID() Get and return message system id
bool setString(const char *key, const char *value)
setString(const char* key, const char* value)
bool setInt(const char *key, int64 value)
setInt(const char* key, int64 value)
DataMessageHeader * data
Pointer to the message's flat memory block (header + user entries).
bool setStatus(uint16 status)
setStatus(uint16 status)
uint16 getStatus()
getStatus()
uint32 getSize()
getSize() Get message size Many types of data of any size can be put into a message as user entries; ...
uint64 getReference()
getReference() Get and return message reference id
bool setReference(uint64 ref)
setReference(uint64 ref) Set message reference
bool setData(const char *key, const char *value, uint32 size)
setData(const char* key, const char* value, uint32 size)
const char * getString(const char *key)
getString(const char* key)
DataMessage * copy()
copy() Creates a full deep copy of the message and returns it.
A parsed or generated HTTP response.
uint8 type
HTTP_* status id of this reply.
A parsed or generated HTTP request (also used for WebSocket upgrade handshakes).
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.
JSONM: a JSON document with attached binary chunks in one contiguous buffer.
uint32 addData(const char *name, const char *data, uint64 size, const char *type)
Append a binary attachment.
char * jmData
Owned serialised container buffer (JSON + attachments).
bool setJSON(const char *json)
Replace the JSON text portion (multipart metadata is regenerated).
One logical network interface: a group of listeners/connections with shared dispatch.
uint64 createWebsocketConnection(const char *url, const char *protocolName, const char *origin=NULL, uint32 timeoutMS=5000)
Open a client WebSocket connection from a full URL.
WebsocketData * waitForWebsocketData(uint64 &conid, uint32 ms)
Wait for the next queued WebSocket message.
bool sendMessage(DataMessage *msg, uint64 conid)
Send a DataMessage.
bool endConnection(uint64 conid)
Gracefully close a connection.
bool sendWebsocketData(WebsocketData *wsData, uint64 conid)
Send a WebSocket frame.
DataMessage * waitForMessage(uint64 &conid, uint32 ms)
Wait for the next queued DataMessage.
Central owner of all channels, listeners and connections in a process.
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://).
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.
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).
Application-facing client of the request system: posts DataMessage requests to one or more RequestGat...
bool receiveHTTPReply(HTTPReply *reply, HTTPRequest *req, NetworkChannel *channel, uint64 conid)
NetworkReceiver hook: HTTP replies (when talking to a gateway over HTTP).
uint64 lastRefID
Last issued client reference id.
static DataMessage * SendRequestAndWaitForReply(RequestClient *client, DataMessage *msg, int timeoutMS)
Convenience: post msg, wait, and return the reply message.
utils::Mutex conMutex
Guards connections.
std::map< uint64, RequestReply * > requestMap
In-flight requests by client reference id.
bool receiveNetworkEvent(NetworkEvent *evt, NetworkChannel *channel, uint64 conid)
NetworkReceiver hook: gateway connect/disconnect events (drives reconnection).
NetworkManager * manager
Owned network stack for gateway links.
bool run()
Worker loop: connection upkeep, outgoing queue, timeouts.
bool receiveMessage(DataMessage *msg, NetworkChannel *channel, uint64 conid)
NetworkReceiver hook: reply messages from gateways, matched to pending requests.
bool finishRequest(RequestReply *reply)
Release a RequestReply obtained from postRequest() back to the client's pool.
RequestReply * postRequest(DataMessage *msg)
Post a request asynchronously.
bool sendRequest(RequestReply *reply)
Serialise and send one request to the best available gateway.
bool reconnect()
Force reconnection of gateway links.
static bool TestServerLogin(const char *address, uint16 port, const char *username, const char *password, const char *reqAfterLogin)
Manual test: log in to a request server and issue a request.
static std::string SendRequestAndWaitForJSON(RequestClient *client, DataMessage *msg, int timeoutMS)
Convenience: post msg on client, wait, and return the reply's JSON text.
uint64 receivedCount
Total replies received.
uint32 sendStatusNow()
Send a status/heartbeat message to all gateways.
utils::WaitQueuePointer< RequestReply * > outQ
Outgoing requests awaiting dispatch.
static std::string GetJSONReplyParameter(const char *json, const char *name)
Extract a named top-level string value from a JSON reply.
bool addGateway(uint32 id, std::string addr, uint16 port, uint8 encryption=NOENC)
Register a gateway to connect to (may be called several times for redundancy).
static DataMessage * CreateRequestMessage(uint8 operation, const char *req, const char *key1=NULL, const char *val1=NULL, const char *key2=NULL, const char *val2=NULL, const char *key3=NULL, const char *val3=NULL, const char *key4=NULL, const char *val4=NULL, const char *key5=NULL, const char *val5=NULL, const char *key6=NULL, const char *val6=NULL, const char *key7=NULL, const char *val7=NULL, const char *key8=NULL, const char *val8=NULL, const char *key9=NULL, const char *val9=NULL, const char *key10=NULL, const char *val10=NULL, const char *key11=NULL, const char *val11=NULL, const char *key12=NULL, const char *val12=NULL, const char *key13=NULL, const char *val13=NULL, const char *key14=NULL, const char *val14=NULL, const char *key15=NULL, const char *val15=NULL, const char *key16=NULL, const char *val16=NULL, const char *key17=NULL, const char *val17=NULL, const char *key18=NULL, const char *val18=NULL, const char *key19=NULL, const char *val19=NULL, const char *key20=NULL, const char *val20=NULL)
Build a request DataMessage from an operation, request name and up to 20 key/value string pairs (NULL...
bool waitForConnection(uint32 timeoutMS)
Block until a gateway connection is established.
friend THREAD_RET THREAD_FUNCTION_CALL RequestClientRun(THREAD_ARG arg)
Thread entry point for the RequestClient worker loop.
bool sendMessageToGateway(DataMessage *msg, RequestGatewayConnection &con)
Send a message on a specific gateway link.
uint32 threadID
Worker thread id.
std::list< RequestGatewayConnection > connections
Configured gateways and their state.
NetworkChannel * channel
Channel carrying the gateway connections.
static RequestReply * SendRequestAndWaitForReplyObject(RequestClient *client, DataMessage *msg, int timeoutMS)
Convenience: post msg, wait, and return the whole RequestReply object.
uint64 sentCount
Total requests sent.
utils::Mutex mutex
Guards request bookkeeping.
Future/handle for one in-flight request: holds the request message, the eventual reply,...
DataMessage * peekRequestMessage()
RequestCallbackFunction callback
bool setRequestMessageCopy(DataMessage *msg)
Attach a copy of the outgoing request message (caller keeps msg).
DataMessage * replyMsg
bool setCallback(RequestCallbackFunction callback)
Register a completion callback (alternative to blocking waits).
bool setStatus(RequestStatus status)
Set the current lifecycle status (wakes waiters when terminal).
DataMessage * getRequestMessageCopy()
bool replyToRequest(DataMessage *msg, RequestStatus status)
Complete this request with a reply (called by the network layer).
RequestStatus getStatus()
bool giveReplyMessage(DataMessage *msg)
Attach the reply message, transferring ownership.
uint64 startTime
When the request was created (ms epoch).
uint32 systemID
Id of the system/gateway this request belongs to.
std::string getStatusText()
RequestStatus waitForResult(uint32 timeoutMS)
Block until the request reaches a terminal status or the timeout expires.
RequestCallbackFunction getCallback()
uint64 finishTime
When a terminal status was set (ms epoch; 0 while pending).
DataMessage * getReplyMessageCopy()
bool isInUse
Pooling flag: false once finishRequest() releases the object for reuse.
DataMessage * requestMsg
utils::Semaphore semaphore
uint64 clientRef
Client-side reference id correlating request and reply.
bool setReplyMessageCopy(DataMessage *msg)
Attach a copy of the reply message (caller keeps msg).
bool giveRequestMessage(DataMessage *msg)
Attach the outgoing request message, transferring ownership.
DataMessage * waitForMessage(uint32 timeoutMS, bool takeMessage=false)
Block for the reply message itself.
DataMessage * peekReplyMessage()
bool isRunning
Set by the worker while its loop is active.
virtual bool stop(uint32 timeout=200)
Ask the worker loop to finish and wait for it to do so.
bool shouldContinue
Loop-continuation flag; cleared by stop().
Sliding-window sample collector with average, variance, standard deviation and median.
Definition Stats.h:43
double getAverage()
Definition Stats.cpp:48
bool add(double val)
Add a sample, evicting the oldest when the window is full.
Definition Stats.cpp:29
friend THREAD_RET THREAD_FUNCTION_CALL TestRequestClientRun(THREAD_ARG arg)
bool init(uint16 port, uint32 gatewayCount, double sendfreq, int32 reconnectMS, uint32 payload, const char *longTestName)
bool init(uint16 port, uint32 gatewayCount, double sendfreq, int32 reconnectMS, uint32 payload, const char *longTestName)
friend THREAD_RET THREAD_FUNCTION_CALL TestWebRequestClientRun(THREAD_ARG arg)
bool init(uint16 port, uint32 gatewayCount, double sendfreq, int32 reconnectMS, uint32 payload, const char *longTestName)
friend THREAD_RET THREAD_FUNCTION_CALL TestWebSocketRequestClientRun(THREAD_ARG arg)
static bool CreateThread(THREAD_FUNCTION func, void *args, uint32 &newID, uint32 reqID=0)
Create a new native thread and start it immediately.
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.
char * data
Owned decoded payload bytes.
bool setData(DataType dataType, bool maskData, const char *data=NULL, uint64 size=0)
Set the payload and build the serialised frame for sending.
uint64 GetTimeNow()
Return the current absolute time (µs since year 0) according to the TMC.
Definition PsyTime.cpp:69
int32 GetTimeAgeMS(uint64 t)
Age of a timestamp relative to now, in milliseconds.
Definition PsyTime.cpp:35
RequestStatus
Lifecycle states of a request as it moves through client, gateway and executor.
void(* RequestCallbackFunction)(RequestReply &reply)
Signature for asynchronous completion callbacks registered on a RequestReply.
THREAD_RET THREAD_FUNCTION_CALL RequestClientRun(THREAD_ARG arg)
Thread entry point for the RequestClient worker loop.
bool Sleep(uint32 ms)
Suspend the calling thread.
Definition Utils.cpp:2802
std::string StringFormat(const char *format,...)
printf into a std::string.
Definition Utils.cpp:6626
const char * GetJSONValue(jsmntok_t *tokens, int tokenCount, const char *json, int token, int &valLength)
Definition jsmn.cpp:347
@ JSMN_UNDEFINED
Definition jsmn.h:29
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, unsigned int num_tokens)
Run JSON parser.
Definition jsmn.cpp:156
void jsmn_init(jsmn_parser *parser)
Create JSON parser over an array of tokens.
Definition jsmn.cpp:311
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.
static bool Test_RequestClient()
THREAD_RET THREAD_FUNCTION_CALL TestRequestClientRun(THREAD_ARG arg)
THREAD_RET THREAD_FUNCTION_CALL TestWebSocketRequestClientRun(THREAD_ARG arg)
THREAD_RET THREAD_FUNCTION_CALL TestWebRequestClientRun(THREAD_ARG arg)
void Register_RequestClient_Tests()
Notification of a connection lifecycle change (connect, disconnect, buffer state.....
uint8 type
NETWORKEVENT_* event type.
RequestQueue longReqQueue
Gateway-side queue of long requests for this peer.
RequestQueue shortReqQueue
Gateway-side queue of short requests for this peer.
uint32 removeStaleRequests(uint32 ttlMS)
Fail and drop requests older than ttlMS from both queues.
uint32 count
Total requests routed via this connection.
Client/executor-side record of one gateway it connects to.
uint8 encryption
NOENC or SSLENC for this link.
uint32 id
Gateway id (configuration key).
uint64 location
Resolved endpoint packed as uint64.
uint64 conID
NetworkManager connection id (0 when disconnected).
std::string addr
Gateway host name or IP.
uint64 lastFailTime
Last failed connect time (ms epoch; drives retry backoff).
uint64 lastConTime
Last successful connect time (ms epoch).
void clear()
Reset to the unconfigured/disconnected state.
JSON parser.
Definition jsmn.h:65
JSON token description.
Definition jsmn.h:51