CMSDK 2.0.1
Cross-platform C++ base library and SDK for the Psyclone AIOS platform
Loading...
Searching...
No Matches
RequestGateway.cpp
Go to the documentation of this file.
1
7#include "RequestGateway.h"
8#include "UnitTestFramework.h"
9
10namespace cmlabs{
11
12
14// Request Queue
16
18 id = 0;
19}
20
22 if (mutex.enter(500, __FUNCTION__))
23 requestQ.clear();
24 mutex.leave();
25}
26
27bool RequestQueue::init(uint32 id) {
28 this->id = id;
29 return true;
30}
31
32bool RequestQueue::addRequest(RequestReply* req, double priority) {
33 if (!req || !req->gatewayRef)
34 return false;
35 if (!mutex.enter(500, __FUNCTION__))
36 return false;
37 std::list<RequestReply*>::iterator i = requestQ.begin(), e = requestQ.end(), p = requestQ.end();
38 while (i != e) {
39 if (((*i) == req) || ((*i)->gatewayRef == req->gatewayRef)) {
40 mutex.leave();
41 return false;
42 }
43 if ((*i)->origin == req->origin)
44 p = i;
45 i++;
46 }
47 if (priority > 0) {
48 if (p == e) {
49 requestQ.push_front(req);
50 //LogPrint(0, LOG_SYSTEM, 2, "--- Priority [%.3f] --> front\n", priority);
51 }
52 else {
53 p++;
54 if (p != e) p++;
55 if ((p != e) && (priority < 0.5)) p++;
56 if ((p != e) && (priority < 0.2)) p++;
57 if (p == e) {
58 requestQ.push_back(req);
59 //LogPrint(0, LOG_SYSTEM, 2, "--- Priority [%.3f] --> end\n", priority);
60 }
61 else {
62 requestQ.insert(p, req);
63 //LogPrint(0, LOG_SYSTEM, 1, "--- Priority [%.3f] --> middle\n", priority);
64 }
65 }
66 }
67 else
68 requestQ.push_back(req);
69 mutex.leave();
70 return true;
71}
72
73
75 if (!mutex.enter(500, __FUNCTION__))
76 return NULL;
77 RequestStatus status;
78 RequestReply* req = NULL;
79 std::list<RequestReply*>::iterator i = requestQ.begin(), e = requestQ.end();
80 while (i != e) {
81 req = *i;
82 if ( (status = req->getStatus()) == RequestStatus::QUEUED) {
83 // mark request as in use
85 mutex.leave();
86 return req;
87 }
88 else if (status == RequestStatus::LOCALERROR) {
89 requestQ.remove(req);
90 delete req;
91 mutex.leave();
92 return NULL;
93 }
94 i++;
95 }
96 mutex.leave();
97 return NULL;
98}
99
101 if (!mutex.enter(500, __FUNCTION__))
102 return NULL;
103 RequestStatus status;
104 RequestReply* req = NULL;
105 std::list<RequestReply*>::iterator i = requestQ.begin(), e = requestQ.end();
106 while (i != e) {
107 req = *i;
108 if ((status = req->getStatus()) == RequestStatus::TIMEOUT) {
109 requestQ.remove(req);
110 mutex.leave();
111 return req;
112 }
113 i++;
114 }
115 mutex.leave();
116 return NULL;
117}
118
119
120//RequestReply* RequestQueue::getRequest(uint64 ref) {
121// if (!mutex.enter(1000))
122// return NULL;
123// RequestReply* req = NULL;
124// std::list<RequestReply*>::iterator i = requestQ.begin(), e = requestQ.end();
125// while (i != e) {
126// req = *i;
127// if (req->gatewayRef == ref) {
128// mutex.leave();
129// return req;
130// }
131// i++;
132// }
133// mutex.leave();
134// return NULL;
135//}
136
138 if (!mutex.enter(500, __FUNCTION__))
139 return false;
140 requestQ.remove(req);
141 mutex.leave();
142 return true;
143}
144
145std::list<RequestReply*> RequestQueue::takeQueue() {
146 std::list<RequestReply*> q;
147 if (!mutex.enter(1000, __FUNCTION__))
148 return q;
149 q.assign(requestQ.begin(), requestQ.end());
150 requestQ.clear();
151 mutex.leave();
152 return q;
153}
154
156 uint32 count = 0;
157 if (!mutex.enter(500, __FUNCTION__))
158 return 0;
159 RequestStatus status;
160 RequestReply* req = NULL;
161 std::list<RequestReply*>::iterator i = requestQ.begin(), e = requestQ.end();
162 while (i != e) {
163 req = *i;
164 status = req->getStatus();
165 if ((status == RequestStatus::PROCESSING) || (status == RequestStatus::SENT)) {
166 if ((int64)GetTimeAgeMS(req->startTime) > (int64)ttlMS) {
168 count++;
169 }
170 }
171 i++;
172 }
173 mutex.leave();
174 return count;
175}
176
177std::string RequestQueue::toXML() {
178 if (!mutex.enter(1000, __FUNCTION__))
179 return "";
180 uint32 p = 0;
181 std::string xml;
182 std::list<RequestReply*>::iterator i = requestQ.begin(), e = requestQ.end();
183 while (i != e) {
184 p++;
185 xml += utils::StringFormat("<entry pos=\"%u\" ref=\"%llu\" origin=\"%u\" systemid=\"%u\" status=\"%s\" age=\"%d\" />\n",
186 p, (*i)->gatewayRef, (*i)->origin, (*i)->systemID, (*i)->getStatusText().c_str(), GetTimeAgeMS((*i)->startTime));
187 i++;
188 }
189 mutex.leave();
190 return xml;
191}
192
193std::string RequestQueue::toJSON() {
194 if (!mutex.enter(1000, __FUNCTION__))
195 return "";
196 uint32 p = 0;
197 std::string json;
198 std::list<RequestReply*>::iterator i = requestQ.begin(), e = requestQ.end();
199 while (i != e) {
200 if (!p) {
201 p++;
202 json += utils::StringFormat("{\"pos\":%u, \"ref\":%llu, \"origin\":%llu, \"systemid\":%u, \"status\":\"%s\", \"age\":%d}\n",
203 p, (*i)->gatewayRef, (*i)->origin, (*i)->systemID, (*i)->getStatusText().c_str(), GetTimeAgeMS((*i)->startTime));
204 }
205 else {
206 p++;
207 json += utils::StringFormat(",{\"pos\":%u, \"ref\":%llu, \"origin\":%llu, \"systemid\":%u, \"status\":\"%s\", \"age\":%d}\n",
208 p, (*i)->gatewayRef, (*i)->origin, (*i)->systemID, (*i)->getStatusText().c_str(), GetTimeAgeMS((*i)->startTime));
209 }
210 i++;
211 }
212 mutex.leave();
213 return json;
214}
215
216
217
218
219
220
221
222
223
224
225
227
228 uint64 lastRefID = 0;
229 uint32 conid = 1;
230
231 RequestQueue reqQ;
232
233 unittest::progress(5, "empty queue");
234 if (reqQ.getCount() != 0) { unittest::fail("RequestQueue test: fresh queue not empty"); return false; }
235 if (reqQ.getNextRequest() != NULL) { unittest::fail("RequestQueue test: getNextRequest on empty queue returned non-NULL"); return false; }
236
237 // Add a single queued request and verify it can be retrieved.
238 unittest::progress(20, "single add/get");
239 RequestReply* reply = new RequestReply();
240 reply->origin = conid;
241 reply->clientRef = 15;
242 reply->gatewayRef = ++lastRefID;
243 reply->giveRequestMessage(new DataMessage());
244 reply->setStatus(QUEUED);
245 if (!reqQ.addRequest(reply)) { unittest::fail("RequestQueue test: addRequest failed for first request"); return false; }
246 if (reqQ.getCount() != 1) { unittest::fail("RequestQueue test: count != 1 after first add"); return false; }
247
248 // Duplicate gatewayRef must be rejected.
249 if (reqQ.addRequest(reply)) { unittest::fail("RequestQueue test: duplicate addRequest unexpectedly succeeded"); return false; }
250
251 // getNextRequest should hand back the queued request and mark it PROCESSING.
252 RequestReply* got = reqQ.getNextRequest();
253 if (got != reply) { unittest::fail("RequestQueue test: getNextRequest did not return the queued request"); return false; }
254 if (got->getStatus() != PROCESSING) { unittest::fail("RequestQueue test: retrieved request not marked PROCESSING"); return false; }
255 // Already PROCESSING (not QUEUED), so a second call finds nothing to dispatch.
256 if (reqQ.getNextRequest() != NULL) { unittest::fail("RequestQueue test: getNextRequest returned a non-QUEUED request"); return false; }
257
258 // Complete it and confirm the queue empties.
259 if (!reqQ.completeRequest(got)) { unittest::fail("RequestQueue test: completeRequest failed"); return false; }
260 if (reqQ.getCount() != 0) { unittest::fail("RequestQueue test: count != 0 after completeRequest"); return false; }
261 delete got;
262
263 // Priority ordering: enqueue a batch, ensure a high-priority insert lands ahead.
264 unittest::progress(45, "priority ordering");
265 const uint32 N = 2000;
266 uint64 t0 = GetTimeNow();
267 for (uint32 k = 0; k < N; k++) {
268 RequestReply* r = new RequestReply();
269 r->origin = 1000 + k; // distinct origins so none collide
270 r->gatewayRef = ++lastRefID;
272 r->setStatus(QUEUED);
273 if (!reqQ.addRequest(r, (k == N - 1) ? 1.0 : 0.0)) { unittest::fail("RequestQueue test: bulk addRequest failed"); return false; }
274 }
275 double addUs = (double)(GetTimeNow() - t0);
276 if (reqQ.getCount() != N) { unittest::fail("RequestQueue test: count mismatch after bulk add"); return false; }
277
278 // The high-priority (last-added) request should now be at the front.
279 unittest::progress(70, "drain queue");
280 RequestReply* front = reqQ.getNextRequest();
281 if (!front || front->origin != (uint64)(1000 + (N - 1))) {
282 unittest::fail("RequestQueue test: high-priority request not at front");
283 return false;
284 }
285
286 // Drain everything via takeQueue and free it.
287 std::list<RequestReply*> drained = reqQ.takeQueue();
288 if (drained.size() != N) { unittest::fail("RequestQueue test: takeQueue returned wrong count"); return false; }
289 if (reqQ.getCount() != 0) { unittest::fail("RequestQueue test: queue not empty after takeQueue"); return false; }
290 for (std::list<RequestReply*>::iterator i = drained.begin(), e = drained.end(); i != e; i++)
291 delete(*i);
292
293 unittest::detail("RequestQueue: added %u requests in %.3f ms\n", N, addUs / 1000.0);
294 unittest::metric("enqueue_throughput", (double)N / addUs * 1e6, "ops/s", true);
295 unittest::metric("avg_enqueue_latency", addUs / (double)N, "us", false);
296
297 unittest::progress(100, "done");
298 return true;
299}
300
301
302
303
304
305
306
307
308
309
310
311
313// RequestGateway
315
316RequestGateway::RequestGateway(uint32 id, const char* version) {
318 if (version)
319 versionString = version;
320 //httpAuth[base64_encode(utils::StringFormat("user2:password"))] = GetTimeNow();
321 cacheFiles = false;
322 replyXML = true;
323 webServerName = "Server";
324 internalAPITitle = "gw/";
325 externalAPITitle = "api/";
326 sslSupport = false;
328 lastRefID = 0;
329 clientSentCount = 0;
331 execSentCount = 0;
333 shouldContinue = true;
334 isRunning = false;
335 this->id = id;
336 channel = NULL;
337 manager = new NetworkManager();
338 longReqLimit = 0;
343 priorityThreshold = 60000;
344 callLogMax = 1000;
345 callLogCount = 0;
346}
347
349 shouldContinue = false;
350 stop();
351
353 utils::Sleep(20);
355 utils::Sleep(20);
356
357 std::map<uint64, RequestReply*>::iterator i, e;
358 mutex.enter(200, __FUNCTION__);
359 for (i = requestMap.begin(), e = requestMap.end(); i != e; i++)
360 delete(i->second);
361 requestMap.clear();
362 mutex.leave();
363
364 delete(manager);
365}
366
367bool RequestGateway::init(const char* sslCertPath, const char* sslKeyPath) {
368 if (sslCertPath && sslKeyPath) {
369 if (!manager->setSSLCertificate(sslCertPath, sslKeyPath)) {
370 LogPrint(0, LOG_SYSTEM, 0, "SSL Certificate or Key not found in paths: %s and %s!", sslCertPath, sslKeyPath);
371 sslSupport = false;
372 return false;
373 }
374 sslSupport = true;
375 }
376
377 if (!mutex.enter(200, __FUNCTION__))
378 return false;
379
381 shouldContinue = false;
382 mutex.leave();
383 return false;
384 }
386 shouldContinue = false;
387 mutex.leave();
388 return false;
389 }
390
391 mutex.leave();
392 return true;
393}
394
396 executorHeartbeatTimeout = timeout;
397 return true;
398}
399
402 return true;
403}
404
405bool RequestGateway::setResponseType(const char* type) {
406 if (!type || !strlen(type))
407 return false;
408 replyXML = (strstr(type, "xml") || strstr(type, "XML"));
409 return true;
410}
411
412bool RequestGateway::setWebServerInfo(const char* name, const char* rootdir, const char* indexfile) {
413 if (!name || !rootdir)
414 return false;
415 webServerName = name;
416 this->rootdir = rootdir;
417 if (indexfile)
418 indexFilename = indexfile;
419 return true;
420}
421
422bool RequestGateway::setExternalAPILabel(const char* label) {
423 if (!label)
424 return false;
425 externalAPITitle = label;
426 return true;
427}
428
429bool RequestGateway::setInternalAPILabel(const char* label) {
430 if (!label)
431 return false;
432 internalAPITitle = label;
433 return true;
434}
435
437 cacheFiles = cache;
438 return true;
439}
440
442 this->maxRequestQueueSize = maxRequestQueueSize;
443 this->maxRequestProcessingSize = maxRequestProcessingSize;
444 this->priorityThreshold = priorityThreshold;
445 return true;
446}
447
448bool RequestGateway::addPort(uint16 port, uint8 encryption, bool enableHTTP, uint32 timeout) {
449 if (!mutex.enter(200, __FUNCTION__))
450 return false;
451
452 if ((encryption == SSLENC) && !sslSupport) {
453 LogPrint(0, LOG_SYSTEM, 0, "SSL encryption not enabled!");
454 mutex.leave();
455 return false;
456 }
457
458 if (enableHTTP) {
459 if (!channel) {
460 channel = manager->createListener(port, encryption, PROTOCOL_HTTP_SERVER, true, timeout, false, 0, this);
461 if (!channel) {
462 mutex.leave();
463 return false;
464 }
465 }
466 else {
467 if (!channel->startListener(0, port, encryption, PROTOCOL_HTTP_SERVER, true, timeout, false)) {
468 mutex.leave();
469 return false;
470 }
471 }
472 }
473
474 if (!channel) {
475 channel = manager->createListener(port, encryption, PROTOCOL_MESSAGE, true, timeout, false, 0, this);
476 if (!channel) {
477 mutex.leave();
478 return false;
479 }
480 }
481 else {
482 if (!channel->startListener(0, port, encryption, PROTOCOL_MESSAGE, true, timeout, false)) {
483 mutex.leave();
484 return false;
485 }
486 }
487
488 mutex.leave();
489 return true;
490}
491
492
493bool RequestGateway::addAuthUser(const char* user, const char* password) {
494 if (!user)
495 return false;
496 if (password)
497 httpAuth[base64_encode(utils::StringFormat("%s:%s", user, password))] = GetTimeNow();
498 else
499 httpAuth[base64_encode(utils::StringFormat("%s:", user, password))] = GetTimeNow();
500 return true;
501}
502
503bool RequestGateway::addGateway(uint32 id, std::string addr, uint16 port) {
504 return true;
505}
506
508 if (!mutex.enter(200, __FUNCTION__))
509 return false;
510 uint64 now = GetTimeNow();
511 std::map<uint64,RequestConnection>::iterator i;
512 if ( (i = executors.find(conid)) != executors.end()) {
513 switch(evt->type) {
515 i->second.lastFailTime = now;
516 break;
518 i->second.lastFailTime = 0;
519 break;
520 default:
521 break;
522 }
523 }
524 else if ( (i = clients.find(conid)) != clients.end()) {
525 switch(evt->type) {
527 i->second.lastFailTime = now;
528 break;
530 i->second.lastFailTime = 0;
531 break;
532 default:
533 break;
534 }
535 }
536 else if ((i = webClients.find(conid)) != webClients.end()) {
537 switch (evt->type) {
539 i->second.lastFailTime = now;
540 break;
542 i->second.lastFailTime = 0;
543 break;
544 default:
545 break;
546 }
547 }
548 else if ((i = webSockets.find(conid)) != webSockets.end()) {
549 switch (evt->type) {
551 i->second.lastFailTime = now;
552 break;
554 i->second.lastFailTime = 0;
555 break;
556 default:
557 break;
558 }
559 }
560 delete(evt);
561 mutex.leave();
562 return true;
563}
564
566 if (!msg) return false;
568 int64 val1, val2;
569 std::map<uint64,RequestConnection>::iterator i;
570 uint64 now = GetTimeNow();
571 const char* req = msg->getString("URI");
572
573 if (!mutex.enter(1000, __FUNCTION__)) {
574 LogPrint(0, LOG_SYSTEM, 0, "Error locking Gateway mutex");
575 delete(msg);
576 return true;
577 }
578
579 msg->setRecvTime(now);
580
581 if ( (i=executors.find(conid)) != executors.end()) {
582 if (req && (strcmp(req, "ExecutorStatus") == 0) &&
583 msg->getInt("ShortQSize", val1) &&
584 msg->getInt("LongQSize", val2) ) {
585 i->second.lastStatusTime = now;
586 i->second.reportedShortReqQSize = (uint32) val1;
587 i->second.reportedLongReqQSize = (uint32) val2;
588 delete(msg);
589 LogPrint(0, LOG_NETWORK, 8, "Got heartbeat from Executor %llu...", i->second.conID);
590 }
591 else {
592 LogPrint(0, LOG_NETWORK, 3, "Got reply on request %llu from Executor %llu...", msg->getReference(), i->second.conID);
594
595 std::map<uint64, RequestReply*>::iterator ii = requestMap.find(msg->getReference());
596 if (ii != requestMap.end())
597 ii->second->setStatus(SUCCESS);
598 replyQ.add(msg);
599 }
600 }
601 else if (clients.find(conid) != clients.end()) {
602 LogPrint(0, LOG_NETWORK, 3, " - Gateway received message from Client %llu (%llu - %s)", conid, msg->getReference(), req);
603 if (req && (strcmp(req, "ClientStatus") == 0)) {
604 // for now do nothing, just testing the connection
605 delete(msg);
606 }
607 else {
608 LogPrint(0, LOG_NETWORK, 3, "Gateway received request from client %llu: '%s'", conid, req);
609 uint64 origin = channel->getRemoteAddress(conid);
610
611 addToRequestQueue(msg, origin, conid, msg->getReference());
612 }
613 }
614 else {
615 if (req && strcmp(req, "ExecutorConnect") == 0) {
616 std::vector<std::string> longRequests = utils::TextListSplit(msg->getString("LongRequests"), ",");
617 if (longRequests.size())
618 longReqNames = longRequests;
619 con = &executors[conid];
620 con->clear();
621 con->conID = conid;
622 con->lastConTime = now;
623 con->location = channel->getRemoteAddress(conid);
624 //executors[conid] = con;
625 LogPrint(0, LOG_SYSTEM, 0, "Executor connected on con: %llu", conid);
626 }
627 else if (req && strcmp(req, "ClientConnect") == 0) {
628 con = &clients[conid];
629 con->clear();
630 con->conID = conid;
631 con->lastConTime = now;
632 con->location = channel->getRemoteAddress(conid);
633 //clients[conid] = con;
634 LogPrint(0, LOG_SYSTEM, 0, "Client connected on con: %llu", conid);
635 }
636 delete(msg);
637 }
638 mutex.leave();
639 return true;
640}
641
642bool RequestGateway::addToRequestQueue(DataMessage* msg, uint64 origin, uint64 conID, uint64 clientRef) {
643
644 msg->setString("REMOTE_ADDR", utils::StringFormat("%u.%u.%u.%u:%u", GETIPADDRESSQUADPORT(origin)).c_str());
645
646 RequestReply* reply = new RequestReply();
647 reply->origin = conID;
648 reply->gatewayRef = ++lastRefID;
649 if (clientRef)
650 reply->clientRef = clientRef;
651 else
652 reply->clientRef = reply->gatewayRef;
653 msg->setReference(reply->gatewayRef);
654 reply->giveRequestMessage(msg);
655 reply->setStatus(QUEUED);
657
658 // Add to requestMap
659 requestMap[reply->gatewayRef] = reply;
660 //printf("Adding request GWID: %llu\n", reply->gatewayRef);
661
662 return addRequestReplyToRequestQueue(reply);
663}
664
666 DataMessage *replyMsg;
667 DataMessage* msg = reply->peekRequestMessage();
668 if (!msg) {
669 LogPrint(0, LOG_SYSTEM, 0, "No request message found!");
670 reply->setStatus(FAILED);
671 replyMsg = new DataMessage();
672 //replyMsg->setReference(reply->clientRef);
673 replyMsg->setReference(reply->gatewayRef);
674 if (replyXML)
675 replyMsg->setString("XML", "<error>No request message available</error>");
676 else
677 replyMsg->setString("JSON", "{\"error\": \"No request message available\"}\n");
678 replyQ.add(replyMsg);
679 return false;
680 }
681
682 // Decide if short or long request
683 const char* reqStr = msg->getString("URI");
684 if (!reqStr)
685 reqStr = msg->getString("REQUEST");
686 if (!reqStr) {
687 LogPrint(0, LOG_SYSTEM, 0, "No request string found!");
688 reply->setStatus(FAILED);
689 replyMsg = new DataMessage();
690 //replyMsg->setReference(reply->clientRef);
691 replyMsg->setReference(reply->gatewayRef);
692 if (replyXML)
693 replyMsg->setString("XML", "<error>No request data available</error>");
694 else
695 replyMsg->setString("JSON", "{\"error\": \"No request data available\"}\n");
696 replyQ.add(replyMsg);
697 return false;
698 }
699
700 // Find the least busy executor
701 uint64 bestID = getBestExecutorID(reqStr, msg->getSize(), reply->isLongReq);
702 if (!bestID) {
703 LogPrint(0, LOG_SYSTEM, 0, "No executors found!");
704 reply->setStatus(FAILED);
705 replyMsg = new DataMessage();
706 //replyMsg->setReference(reply->clientRef);
707 replyMsg->setReference(reply->gatewayRef);
708 if (replyXML)
709 replyMsg->setString("XML", "<error>No processors available</error>");
710 else
711 replyMsg->setString("JSON", "{\"error\": \"No processors available\"}\n");
712 replyQ.add(replyMsg);
713 return false;
714 }
715
716 // Add to executor queue
717 RequestConnection* reqCon = &executors[bestID];
718 if (!reqCon) {
719 LogPrint(0, LOG_SYSTEM, 0, "Executors %llu not found!", bestID);
720 reply->setStatus(FAILED);
721 replyMsg = new DataMessage();
722 //replyMsg->setReference(reply->clientRef);
723 replyMsg->setReference(reply->gatewayRef);
724 if (replyXML)
725 replyMsg->setString("XML", "<error>No processors available</error>");
726 else
727 replyMsg->setString("JSON", "{\"error\": \"No processors available\"}\n");
728 replyQ.add(replyMsg);
729 return false;
730 }
731
732 reply->processor = bestID;
733
734 double conPriority = 0;
735 std::map<uint64, RequestConnection>::iterator i;
736 if ( ((i = clients.find(reply->origin)) != clients.end()) || ((i = webClients.find(reply->origin)) != webClients.end())
737 || ((i = webSockets.find(reply->origin)) != webSockets.end())) {
738 if (i->second.lastConTime) {
739 int32 conTime = GetTimeAgeMS(i->second.lastConTime);
740 if (priorityThreshold && (conTime < (int64)priorityThreshold)) {
741 conPriority = (((double)priorityThreshold) - conTime) / priorityThreshold;
742 }
743 }
744 }
745
746 if (reply->isLongReq) {
747 if (reqCon->longReqQueue.getCount() >= maxRequestQueueSize) {
748 LogPrint(0, LOG_SYSTEM, 0, "All long executors too busy (%llu), rejecting request ID %llu", bestID, reply->gatewayRef);
749 reply->setStatus(TOOBUSY);
750 reply->processor = 0;
751 replyMsg = new DataMessage();
752 //replyMsg->setReference(reply->clientRef);
753 replyMsg->setReference(reply->gatewayRef);
754 if (replyXML)
755 replyMsg->setString("XML", "<error>All long processors too busy</error>");
756 else
757 replyMsg->setString("JSON", "{\"error\": \"All long processors too busy\"}\n");
758 replyQ.add(replyMsg);
759 return false;
760 }
761 reqCon->longReqQueue.addRequest(reply, conPriority);
762
763 /*
764 Long requests are added to the relevant queue
765 - when added check if we can send it right away, if so, add to execQ
766 - if not, leave it in the relevant queue
767 - when reply comes back from executor to free up a space
768 choose the next one in the queue and add to execQ
769 - periodically check queues for missed slots
770 */
771
773 reply->setStatus(PROCESSING);
774 reqCon->longReqQProcessingSize++;
775 execQ.add(reply);
776 LogPrint(0, LOG_SYSTEM, 3, "Sending LONG request %llu to Executor %llu immediately (%u)...", reply->gatewayRef, reply->processor, reqCon->longReqQueue.getCount());
777 }
778 else {
779 LogPrint(0, LOG_SYSTEM, 3, "Added LONG request %llu to Executor %llu queue (%u)...", reply->gatewayRef, reply->processor, reqCon->longReqQueue.getCount());
780 }
781 }
782 else {
783 if (reqCon->shortReqQueue.getCount() >= maxRequestQueueSize) {
784 LogPrint(0, LOG_SYSTEM, 0, "All short executors too busy (%llu), rejecting request!", bestID);
785 reply->setStatus(TOOBUSY);
786 reply->processor = 0;
787 replyMsg = new DataMessage();
788 //replyMsg->setReference(reply->clientRef);
789 replyMsg->setReference(reply->gatewayRef);
790 if (replyXML)
791 replyMsg->setString("XML", "<error>All short processors too busy</error>");
792 else
793 replyMsg->setString("JSON", "{\"error\": \"All short processors too busy\"}\n");
794 replyQ.add(replyMsg);
795 return false;
796 }
797 reqCon->shortReqQueue.addRequest(reply, conPriority);
798
799 // Short requests are added to the relevant queue and put directly into the execQ for sending
800 reply->setStatus(PROCESSING);
801 execQ.add(reply);
802 //LogPrint(0, LOG_SYSTEM, 0, "Sending SHORT request %llu to Executor %llu immediately (%u)...", reply->gatewayRef, reply->processor, reqCon->shortReqQueue.getCount());
803 }
804 return true;
805}
806
807
809// const char* requestString = req->getRequest();
810 const char* requestString = req->getURI();
811 if (!requestString || !strlen(requestString))
812 return false;
813
814 uint64 now = GetTimeNow();
815 requestString++; // skip past the first /
816
817 HTTPReply* replyPage = NULL;
818 //char* exttype;
819
820// uint64 t1 = GetTimeNow();
821
822 if (!mutex.enter(1000, __FUNCTION__))
823 return false;
824
825// uint64 t2 = GetTimeNow();
826
827// uint32 contentSize = 0;
828// utils::WriteAFile("d:/requestin.dat", req->getRawContent(contentSize), contentSize, true);
829
830 //uint64 start = GetTimeNow();
831 if (strstr(requestString, internalAPITitle.c_str()) == requestString) {
832 callInternalAPI(requestString + internalAPITitle.length(), req, channel, conid);
833 //printf("*** Internal API: %.3fms\n", (double)GetTimeAge(start)/1000.0);
834 delete(req);
835 mutex.leave();
836 //printf("\n ----- HTTP Internal took %s (waited: %s)\n\n",
837 // PrintTimeDifString(GetTimeAge(t2)).c_str(),
838 // PrintTimeDifString(t2-t1).c_str());
839 return true;
840 }
841 else if (rootdir.length()) {
842 //exttype = new char[1024];
843 //utils::strcpyavail(exttype, "text/html", 1024, true);
844 if ( (!requestString || !strlen(requestString)) && indexFilename.length()) {
845 LogPrint(0, LOG_NETWORK, 3, "Serving up root index file: %s", indexFilename.c_str());
846 replyPage = new HTTPReply();
847 replyPage->createFromFile(GetTimeNow(), webServerName.c_str(), req->ifModifiedSince, true, cacheFiles, (rootdir+indexFilename).c_str());
848
849 addToCallLog(++callLogCount, 2, 3, false, req->source, 0, 0, 0, req->endReceiveTime, GetTimeNow(), 0, GetTimeAge(now), req->type, req->getSize(), replyPage->getSize(), "Success",
850 utils::StringFormat("Index: %s", indexFilename.c_str()).c_str(), req->getHeaderEntry("User-Agent"), "");
851
852 //printf("*** Read default file: %.3fms\n", (double)GetTimeAge(start)/1000.0);
853 //if (!(html = utils::ReadAFileString(rootdir+indexFilename)).size())
854 // html = utils::StringFormat("Could read index file...");
855 }
856 else if (strstr(requestString, externalAPITitle.c_str()) == requestString) {
857 LogPrint(0, LOG_NETWORK, 3, "Serving up API call: %s", requestString);
858 callExternalAPI(requestString+externalAPITitle.length(), req, channel, conid);
859 //printf("*** External API: %.3fms\n", (double)GetTimeAge(start)/1000.0);
860 delete(req);
861 mutex.leave();
862 //printf("\n ----- HTTP Executor took %s (waited: %s)\n\n",
863 // PrintTimeDifString(GetTimeAge(t2)).c_str(),
864 // PrintTimeDifString(t2 - t1).c_str());
865 return true;
866 }
867 else { // if (strstr(requestString, "html/") == requestString) {
868 LogPrint(0, LOG_NETWORK, 3, "Serving up hosted file for request: %s", requestString);
869 replyPage = new HTTPReply();
870 replyPage->createFromFile(GetTimeNow(), webServerName.c_str(), req->ifModifiedSince, true, cacheFiles, (rootdir+requestString).c_str());
871
872 addToCallLog(++callLogCount, 2, 3, false, req->source, 0, 0, 0, req->endReceiveTime, GetTimeNow(), 0, GetTimeAge(now), req->type, req->getSize(), replyPage->getSize(), "Success",
873 utils::StringFormat("File: %s", requestString).c_str(), req->getHeaderEntry("User-Agent"), "");
874
875 //printf("*** Read file '%s': %.3fms\n", requestString, (double)GetTimeAge(start)/1000.0);
876 //const char* filename = requestString;
877 //if ( utils::TextEndsWith(filename, ".html", false) || utils::TextEndsWith(filename, ".txt", false) ) {
878 // if (!(html = utils::ReadAFileString(rootdir+filename)).size())
879 // html = utils::StringFormat("Could read file '%s'...", filename);
880 //}
881 //else {
882 // if ( !(data = utils::ReadAFile((rootdir+filename).c_str(), datasize, true)))
883 // html = utils::StringFormat("Could read file '%s'...", filename);
884 // else {
885 // if (!html::GetMimeType(exttype, filename, 1024))
886 // html = utils::StringFormat("Unsupported mime type for file '%s'...", filename);
887 // }
888 //}
889 }
890 //if (!replyPage) {
891 // if (data) {
892 // replyPage = new HTTPReply();
893 // replyPage->createPage(HTTP_OK, GetTimeNow(), webServerName.c_str(), 0, true, cacheFiles, exttype, data, datasize);
894 // }
895 // else if (html.length()) {
896 // replyPage = new HTTPReply();
897 // replyPage->createPage(HTTP_OK, GetTimeNow(), webServerName.c_str(), 0, true, cacheFiles, exttype, html.c_str());
898 // }
899 //}
900 if (replyPage) {
901 mutex.leave();
902 channel->sendHTTPReply(replyPage, conid);
903 delete(replyPage);
904 //delete [] data;
905 //delete [] exttype;
906 delete(req);
907 return true;
908 }
909 else {
910 //delete [] data;
911 //delete [] exttype;
912 }
913 }
914 mutex.leave();
915 delete(req);
916 //printf("*** No reply: %.3fms\n", (double)GetTimeAge(start)/1000.0);
917 return false;
918
919}
920
921
922
923
924
925
926
927
928
930
931 if (!wsData)
932 return false;
933
934
935 JSONM* jm = new JSONM(wsData->data, wsData->contentSize);
936 if (!jm->isValid()) {
937 LogPrint(0, LOG_NETWORK, 0, "Invalid Websocket request received (%llu):\n%s", wsData->contentSize, wsData->data);
938 delete jm;
939 return false;
940 }
941 std::string json = jm->getJSON();
942
943 LogPrint(0, LOG_NETWORK, 3, "---- WebSocketData JSON size: %u data size: %llu data count: %llu\n%s\n",
944 (uint32)json.length(), jm->jmSize - json.length(), jm->entries.size(), json.c_str());
945
946 //"operation": "POST",
947 //"uri" : "/processing",
948 //"authorization" : "abcdefg",
949 //"requestid" : 223,
950
951 jsmn_parser parser;
952 jsmntok_t* tokens = new jsmntok_t[1024];
953 int tokenCount = 0;
954 jsmn_init(&parser);
955 if ((tokenCount = jsmn_parse(&parser, json.c_str(), json.length(), tokens, 1024)) <= 0) {
956 // No JSON found
957 LogPrint(0, LOG_NETWORK, 0, "Invalid Websocket JSON request received (%llu):\n%s", wsData->contentSize, json.c_str());
958 delete jm;
959 delete[] tokens;
960 return false;
961 }
962
963 std::string requestString = GetJSONChildValueString(tokens, tokenCount, json.c_str(), "uri", 0, JSMN_UNDEFINED);
964 uint64 ref = GetJSONChildValueUint64(tokens, tokenCount, json.c_str(), "requestid", 0);
965 std::string operation = GetJSONChildValueString(tokens, tokenCount, json.c_str(), "operation", 0, JSMN_UNDEFINED);
966 std::string authorization = GetJSONChildValueString(tokens, tokenCount, json.c_str(), "authorization", 0, JSMN_UNDEFINED);
967
968 if (!requestString.length() || !ref || !operation.length()) {
969 LogPrint(0, LOG_NETWORK, 0, "Invalid Websocket JSON request received, required JSON values not found (%llu):\n%s", wsData->contentSize, wsData->data);
970 delete jm;
971 delete[] tokens;
972 return false;
973 }
974
975 // convert to DataMessage
976 DataMessage* msg = jm->convertToMessage();
977 if (!msg) {
978 LogPrint(0, LOG_NETWORK, 0, "Invalid Websocket request received, message unknown");
979 delete jm;
980 delete[] tokens;
981 return false;
982 }
983
984 if (stricmp(operation.c_str(), "post") == 0)
985 msg->setInt("HTTP_OPERATION", HTTP_POST);
986 else if (stricmp(operation.c_str(), "get") == 0)
987 msg->setInt("HTTP_OPERATION", HTTP_GET);
988 else if (stricmp(operation.c_str(), "put") == 0)
989 msg->setInt("HTTP_OPERATION", HTTP_PUT);
990 else if (stricmp(operation.c_str(), "delete") == 0)
991 msg->setInt("HTTP_OPERATION", HTTP_DELETE);
992 else {
993 LogPrint(0, LOG_NETWORK, 0, "Invalid Websocket operation received '%s'", operation.c_str());
994 delete jm;
995 delete[] tokens;
996 return false;
997 }
998
999 msg->setString("REQUEST", requestString.c_str());
1000 msg->setString("URI", requestString.c_str());
1001 msg->setString("Authorization", authorization.c_str());
1002
1003 RequestConnection *con;
1004 std::map<uint64, RequestConnection>::iterator i;
1005 //RequestReply* reply;
1006 uint64 now = GetTimeNow();
1007
1008 if (!mutex.enter(1000, __FUNCTION__)) {
1009 LogPrint(0, LOG_SYSTEM, 0, "Error locking Gateway mutex");
1010 delete(msg);
1011 delete jm;
1012 delete[] tokens;
1013 return true;
1014 }
1015
1016 if ((i = webSockets.find(conid)) == webSockets.end()) {
1017 con = &webSockets[conid];
1018 con->clear();
1019 con->conID = conid;
1020 con->lastConTime = now;
1021 }
1022
1023 addToRequestQueue(msg, wsData->source, conid, ref);
1024
1025 mutex.leave();
1026 delete jm;
1027 delete[] tokens;
1028 return true;
1029}
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042bool RequestGateway::callExternalAPI(const char* apiName, HTTPRequest* req, NetworkChannel* channel, uint64 conid) {
1043 RequestConnection *con;
1044 std::map<uint64,RequestConnection>::iterator i;
1045 //RequestReply* reply;
1046 uint64 now = GetTimeNow();
1047 DataMessage* msg;
1048 uint64 ref = utils::Ascii2Uint64(req->getParameter("Reference"));
1049
1050 if ( (i=webClients.find(conid)) == webClients.end()) {
1051 con = &webClients[conid];
1052 con->clear();
1053 con->conID = conid;
1054 con->lastConTime = now;
1055 //webClients[conid] = con;
1056 }
1057
1058 // convert to DataMessage
1059 msg = req->convertToMessage();
1060// msg->setString("REQUEST", apiName);
1061 msg->setString("URI", apiName);
1062
1063 addToRequestQueue(msg, req->source, conid, ref);
1064 return true;
1065}
1066
1067bool RequestGateway::callInternalAPI(const char* apiName, HTTPRequest* req, NetworkChannel* channel, uint64 conid) {
1068 HTTPReply* reply = new HTTPReply();
1069 std::string xml, subXML, json, execJSON, clientJSON, webClientJSON, webSocketJSON;
1070 std::map<uint64,RequestConnection>::iterator i,e;
1071 std::list<CallLogEntry*>::reverse_iterator ic, ec;
1072 uint32 clientCount = 0, execCount = 0, webClientCount = 0, webSocketCount = 0;
1073 uint64 now = GetTimeNow();
1074 uint32 ms1 = 1000, ms2 = 10000, ms3 = 60000;
1075 double lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3;
1076 double svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3;
1077
1078 if (httpAuth.size()) {
1079 // https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication
1080 const char* auth = req->getBasicAuthorization();
1081 if (!auth || (httpAuth.find(auth) == httpAuth.end())) {
1082 reply = HTTPReply::CreateAuthorizationReply("Gateway");
1083 // mutex.leave();
1084 channel->sendHTTPReply(reply, conid);
1085 //delete(req);
1086
1087 addToCallLog(++callLogCount, 2, 3, false, req->source, 0, 0, 0, req->endReceiveTime, GetTimeNow(), 0, GetTimeAge(now), req->type, req->getSize(), reply->getSize(), "Auth Failed",
1088 utils::StringFormat("Internal: %s", apiName).c_str(), req->getHeaderEntry("User-Agent"), "");
1089
1090 delete(reply);
1091 return true;
1092 }
1093 }
1094
1095 if (((stricmp(apiName, "status") == 0)) || utils::TextStartsWith(apiName, "status?")) {
1096 svalPerSec1 = scountPerSec1 = svalPerSec2 = scountPerSec2 = svalPerSec3 = scountPerSec3 =
1097 lvalPerSec1 = lcountPerSec1 = lvalPerSec2 = lcountPerSec2 = lvalPerSec3 = lcountPerSec3 = 0;
1098 for (i=executors.begin(), e=executors.end(); i!=e; i++) {
1099 if (!i->second.lastFailTime) {
1100 //i->second.shortAvgStats.getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1101 //i->second.longAvgStats.getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1102 if (replyXML) {
1103 subXML += utils::StringFormat("<executor id=\"%llu\" location=\"%u.%u.%u.%u:%u\" shortQueue=\"%u\" longQueue=\"%u\" count=\"%u\" uptime=\"%s\" statustime=\"%s\" />\n",
1104 i->second.conID,
1105 GETIPADDRESSQUADPORT(i->second.location),
1106 i->second.shortReqQueue.getCount(),
1107 i->second.longReqQueue.getCount(),
1108 i->second.count,
1109 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str(),
1110 PrintTimeDifString(GetTimeAge(i->second.lastStatusTime)).c_str()
1111 );
1112 }
1113 else {
1114 if (execJSON.length())
1115 execJSON += ",";
1116 execJSON += utils::StringFormat("{\"id\":%llu, \"location\":\"%u.%u.%u.%u:%u\", \"shortQueue\":%u, \"longQueue\":%u, \"count\":%u, \"uptime\":\"%s\", \"statustime\":\"%s\" }\n",
1117 i->second.conID,
1118 GETIPADDRESSQUADPORT(i->second.location),
1119 i->second.shortReqQueue.getCount(),
1120 i->second.longReqQueue.getCount(),
1121 i->second.count,
1122 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str(),
1123 PrintTimeDifString(GetTimeAge(i->second.lastStatusTime)).c_str()
1124 );
1125 }
1126 execCount++;
1127 }
1128 }
1129 for (i=clients.begin(), e=clients.end(); i!=e; i++) {
1130 if (!i->second.lastFailTime) {
1131 //shortAvgStatsClients[i->second.conID].getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1132 //longAvgStatsClients[i->second.conID].getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1133 //i->second.shortAvgStats.getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1134 //i->second.longAvgStats.getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1135 if (replyXML) {
1136 subXML += utils::StringFormat("<client id=\"%llu\" location=\"%u.%u.%u.%u:%u\" count=\"%u\" uptime=\"%s\" />\n",
1137 i->second.conID,
1138 GETIPADDRESSQUADPORT(i->second.location),
1139 i->second.count,
1140 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str()
1141 );
1142 }
1143 else {
1144 if (clientJSON.length())
1145 clientJSON += ",";
1146 clientJSON += utils::StringFormat("{\"id\":%llu, \"location\":\"%u.%u.%u.%u:%u\", \"count\":%u, \"uptime\":\"%s\" }\n",
1147 i->second.conID,
1148 GETIPADDRESSQUADPORT(i->second.location),
1149 i->second.count,
1150 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str()
1151 );
1152 }
1153 clientCount++;
1154 }
1155 }
1156 for (i = webClients.begin(), e = webClients.end(); i != e; i++) {
1157 if (!i->second.lastFailTime) {
1158 //shortAvgStatsWeb[i->second.conID].getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1159 //longAvgStatsWeb[i->second.conID].getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1160 //i->second.shortAvgStats.getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1161 //i->second.longAvgStats.getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1162 if (replyXML) {
1163 subXML += utils::StringFormat("<webclient id=\"%llu\" location=\"%u.%u.%u.%u:%u\" count=\"%u\" uptime=\"%s\" />\n",
1164 i->second.conID,
1165 GETIPADDRESSQUADPORT(i->second.location),
1166 i->second.count,
1167 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str()
1168 );
1169 }
1170 else {
1171 if (webClientJSON.length())
1172 webClientJSON += ",";
1173 webClientJSON += utils::StringFormat("{\"id\":%llu, \"location\":\"%u.%u.%u.%u:%u\", \"count\":%u, \"uptime\":\"%s\" }\n",
1174 i->second.conID,
1175 GETIPADDRESSQUADPORT(i->second.location),
1176 i->second.count,
1177 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str()
1178 );
1179 }
1180 webClientCount++;
1181 }
1182 }
1183 for (i = webSockets.begin(), e = webSockets.end(); i != e; i++) {
1184 if (!i->second.lastFailTime) {
1185 //shortAvgStatsWeb[i->second.conID].getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1186 //longAvgStatsWeb[i->second.conID].getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1187 //i->second.shortAvgStats.getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1188 //i->second.longAvgStats.getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1189 if (replyXML) {
1190 subXML += utils::StringFormat("<websocket id=\"%llu\" location=\"%u.%u.%u.%u:%u\" count=\"%u\" uptime=\"%s\" />\n",
1191 i->second.conID,
1192 GETIPADDRESSQUADPORT(i->second.location),
1193 i->second.count,
1194 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str()
1195 );
1196 }
1197 else {
1198 if (webSocketJSON.length())
1199 webSocketJSON += ",";
1200 webSocketJSON += utils::StringFormat("{\"id\":%llu, \"location\":\"%u.%u.%u.%u:%u\", \"count\":%u, \"uptime\":\"%s\" }\n",
1201 i->second.conID,
1202 GETIPADDRESSQUADPORT(i->second.location),
1203 i->second.count,
1204 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str()
1205 );
1206 }
1207 webSocketCount++;
1208 }
1209 }
1210 shortAvgStats.getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1211 longAvgStats.getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1212 if (replyXML)
1213 xml = utils::StringFormat("<status uptime=\"%s\" version=\"%s\" executors=\"%u\" clients=\"%u\" webclients=\"%u\" websockets=\"%u\" maxqueuesize=\"%u\" minqueuesize=\"%u\" "
1214 "shortBytesPerSec1=\"%.3f\" shortCountPerSec1=\"%.3f\" shortBytesPerSec10=\"%.3f\" shortCountPerSec10=\"%.3f\" shortBytesPerSec60=\"%.3f\" shortCountPerSec60=\"%.3f\" "
1215 "longBytesPer1Sec=\"%.3f\" longCountPer1Sec=\"%.3f\" longBytesPer10Sec=\"%.3f\" longCountPer10Sec=\"%.3f\" longBytesPer60Sec=\"%.3f\" longCountPer60Sec=\"%.3f\" "
1216 ">\n%s</status>\n",
1217 PrintTimeDifString(GetTimeAge(systemStartTime)).c_str(), versionString.c_str(), execCount, clientCount, webClientCount, webSocketCount,
1219 svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3,
1220 lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3,
1221 subXML.c_str());
1222 else
1223 json = utils::StringFormat("{\"uptime\":\"%s\", \"version\":\"%s\", \"executorcount\":%u, \"clientcount\":%u, \"webclientcount\":%u, \"websocketcount\":%u, \"maxqueuesize\":%u, \"minqueuesize\":%u, "
1224 "\"shortBytesPerSec1\": %.3f, \"shortCountPerSec1\": %.3f, \"shortBytesPerSec10\": %.3f, \"shortCountPerSec10\": %.3f, \"shortBytesPerSec60\": %.3f, \"shortCountPerSec60\": %.3f, "
1225 "\"longBytesPer1Sec\": %.3f, \"longCountPer1Sec\": %.3f, \"longBytesPer10Sec\": %.3f, \"longCountPer10Sec\": %.3f, \"longBytesPer60Sec\": %.3f, \"longCountPer60Sec\": %.3f, "
1226 "\"executors\":[%s], \"clients\":[%s], \"webclients\":[%s], \"websockets\":[%s]}\n",
1227 PrintTimeDifString(GetTimeAge(systemStartTime)).c_str(), versionString.c_str(), execCount, clientCount, webClientCount, webSocketCount,
1229 svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3,
1230 lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3,
1231 execJSON.c_str(), clientJSON.c_str(), webClientJSON.c_str(), webSocketJSON.c_str());
1232 }
1233 else if (((stricmp(apiName, "extstatus") == 0)) || utils::TextStartsWith(apiName, "extstatus?")) {
1234 svalPerSec1 = scountPerSec1 = svalPerSec2 = scountPerSec2 = svalPerSec3 = scountPerSec3 =
1235 lvalPerSec1 = lcountPerSec1 = lvalPerSec2 = lcountPerSec2 = lvalPerSec3 = lcountPerSec3 = 0;
1236 for (i = executors.begin(), e = executors.end(); i != e; i++) {
1237 if (!i->second.lastFailTime) {
1238 i->second.shortAvgStats.getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1239 i->second.longAvgStats.getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1240 if (replyXML) {
1241 subXML += utils::StringFormat("<executor id=\"%llu\" location=\"%u.%u.%u.%u:%u\" shortqueuesize=\"%u\" longqueuesize=\"%u\" count=\"%u\" uptime=\"%s\" statustime=\"%s\" "
1242 "shortBytesPerSec1=\"%.3f\" shortCountPerSec1=\"%.3f\" shortBytesPerSec10=\"%.3f\" shortCountPerSec10=\"%.3f\" shortBytesPerSec60=\"%.3f\" shortCountPerSec60=\"%.3f\" "
1243 "longBytesPer1Sec=\"%.3f\" longCountPer1Sec=\"%.3f\" longBytesPer10Sec=\"%.3f\" longCountPer10Sec=\"%.3f\" longBytesPer60Sec=\"%.3f\" longCountPer60Sec=\"%.3f\" ",
1244 ">\n<shortqueue>\n%s</shortqueue>\n<longqueue>\n%s</longqueue></executor>\n",
1245 i->second.conID,
1246 GETIPADDRESSQUADPORT(i->second.location),
1247 i->second.shortReqQueue.getCount(),
1248 i->second.longReqQueue.getCount(),
1249 i->second.count,
1250 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str(),
1251 PrintTimeDifString(GetTimeAge(i->second.lastStatusTime)).c_str(),
1252 svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3,
1253 lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3,
1254 i->second.shortReqQueue.toXML().c_str(),
1255 i->second.longReqQueue.toXML().c_str()
1256 );
1257 }
1258 else {
1259 if (execJSON.length())
1260 execJSON += ",";
1261 execJSON += utils::StringFormat("{\"id\":%llu, \"location\":\"%u.%u.%u.%u:%u\", \"shortqueuesize\":%u, \"longqueuesize\":%u, \"count\":%u, \"uptime\":\"%s\", \"statustime\":\"%s\", "
1262 " \"shortBytesPerSec1\": %.3f, \"shortCountPerSec1\": %.3f, \"shortBytesPerSec10\": %.3f, \"shortCountPerSec10\": %.3f, \"shortBytesPerSec60\": %.3f, \"shortCountPerSec60\": %.3f, "
1263 " \"longBytesPer1Sec\": %.3f, \"longCountPer1Sec\": %.3f, \"longBytesPer10Sec\": %.3f, \"longCountPer10Sec\": %.3f, \"longBytesPer60Sec\": %.3f, \"longCountPer60Sec\": %.3f, "
1264 " \"shortqueue\":[%s], \"longqueue\":[%s] }\n",
1265 i->second.conID,
1266 GETIPADDRESSQUADPORT(i->second.location),
1267 i->second.shortReqQueue.getCount(),
1268 i->second.longReqQueue.getCount(),
1269 i->second.count,
1270 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str(),
1271 PrintTimeDifString(GetTimeAge(i->second.lastStatusTime)).c_str(),
1272 svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3,
1273 lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3,
1274 i->second.shortReqQueue.toJSON().c_str(),
1275 i->second.longReqQueue.toJSON().c_str()
1276 );
1277 }
1278 execCount++;
1279 }
1280 }
1281 for (i = clients.begin(), e = clients.end(); i != e; i++) {
1282 if (!i->second.lastFailTime) {
1283 //shortAvgStatsClients[i->second.conID].getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1284 //longAvgStatsClients[i->second.conID].getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1285 i->second.shortAvgStats.getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1286 i->second.longAvgStats.getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1287 if (replyXML) {
1288 subXML += utils::StringFormat("<client id=\"%llu\" location=\"%u.%u.%u.%u:%u\" count=\"%u\" uptime=\"%s\" "
1289 "shortBytesPerSec1=\"%.3f\" shortCountPerSec1=\"%.3f\" shortBytesPerSec10=\"%.3f\" shortCountPerSec10=\"%.3f\" shortBytesPerSec60=\"%.3f\" shortCountPerSec60=\"%.3f\" "
1290 "longBytesPer1Sec=\"%.3f\" longCountPer1Sec=\"%.3f\" longBytesPer10Sec=\"%.3f\" longCountPer10Sec=\"%.3f\" longBytesPer60Sec=\"%.3f\" longCountPer60Sec=\"%.3f\" ",
1291 "/>\n",
1292 i->second.conID,
1293 GETIPADDRESSQUADPORT(i->second.location),
1294 i->second.count,
1295 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str(),
1296 svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3,
1297 lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3
1298 );
1299 }
1300 else {
1301 if (clientJSON.length())
1302 clientJSON += ",";
1303 clientJSON += utils::StringFormat("{\"id\":%llu, \"location\":\"%u.%u.%u.%u:%u\", \"count\":%u, \"uptime\":\"%s\", "
1304 " \"shortBytesPerSec1\": %.3f, \"shortCountPerSec1\": %.3f, \"shortBytesPerSec10\": %.3f, \"shortCountPerSec10\": %.3f, \"shortBytesPerSec60\": %.3f, \"shortCountPerSec60\": %.3f, "
1305 " \"longBytesPer1Sec\": %.3f, \"longCountPer1Sec\": %.3f, \"longBytesPer10Sec\": %.3f, \"longCountPer10Sec\": %.3f, \"longBytesPer60Sec\": %.3f, \"longCountPer60Sec\": %.3f "
1306 "}\n",
1307 i->second.conID,
1308 GETIPADDRESSQUADPORT(i->second.location),
1309 i->second.count,
1310 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str(),
1311 svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3,
1312 lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3
1313 );
1314 }
1315 clientCount++;
1316 }
1317 }
1318 for (i = webClients.begin(), e = webClients.end(); i != e; i++) {
1319 if (!i->second.lastFailTime) {
1320 //shortAvgStatsWeb[i->second.conID].getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1321 //longAvgStatsWeb[i->second.conID].getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1322 i->second.shortAvgStats.getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1323 i->second.longAvgStats.getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1324 if (replyXML) {
1325 subXML += utils::StringFormat("<webclient id=\"%llu\" location=\"%u.%u.%u.%u:%u\" count=\"%u\" uptime=\"%s\" "
1326 "shortBytesPerSec1=\"%.3f\" shortCountPerSec1=\"%.3f\" shortBytesPerSec10=\"%.3f\" shortCountPerSec10=\"%.3f\" shortBytesPerSec60=\"%.3f\" shortCountPerSec60=\"%.3f\" "
1327 "longBytesPer1Sec=\"%.3f\" longCountPer1Sec=\"%.3f\" longBytesPer10Sec=\"%.3f\" longCountPer10Sec=\"%.3f\" longBytesPer60Sec=\"%.3f\" longCountPer60Sec=\"%.3f\" "
1328 "/>\n",
1329 i->second.conID,
1330 GETIPADDRESSQUADPORT(i->second.location),
1331 i->second.count,
1332 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str(),
1333 svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3,
1334 lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3
1335 );
1336 }
1337 else {
1338 if (webClientJSON.length())
1339 webClientJSON += ",";
1340 webClientJSON += utils::StringFormat("{\"id\":%llu, \"location\":\"%u.%u.%u.%u:%u\", \"count\":%u, \"uptime\":\"%s\", "
1341 " \"shortBytesPerSec1\": %.3f, \"shortCountPerSec1\": %.3f, \"shortBytesPerSec10\": %.3f, \"shortCountPerSec10\": %.3f, \"shortBytesPerSec60\": %.3f, \"shortCountPerSec60\": %.3f, "
1342 " \"longBytesPer1Sec\": %.3f, \"longCountPer1Sec\": %.3f, \"longBytesPer10Sec\": %.3f, \"longCountPer10Sec\": %.3f, \"longBytesPer60Sec\": %.3f, \"longCountPer60Sec\": %.3f "
1343 "}\n",
1344 i->second.conID,
1345 GETIPADDRESSQUADPORT(i->second.location),
1346 i->second.count,
1347 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str(),
1348 svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3,
1349 lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3
1350 );
1351 }
1352 webClientCount++;
1353 }
1354 }
1355 for (i = webSockets.begin(), e = webSockets.end(); i != e; i++) {
1356 if (!i->second.lastFailTime) {
1357 //shortAvgStatsWeb[i->second.conID].getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1358 //longAvgStatsWeb[i->second.conID].getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1359 i->second.shortAvgStats.getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1360 i->second.longAvgStats.getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1361 if (replyXML) {
1362 subXML += utils::StringFormat("<websocket id=\"%llu\" location=\"%u.%u.%u.%u:%u\" count=\"%u\" uptime=\"%s\" "
1363 "shortBytesPerSec1=\"%.3f\" shortCountPerSec1=\"%.3f\" shortBytesPerSec10=\"%.3f\" shortCountPerSec10=\"%.3f\" shortBytesPerSec60=\"%.3f\" shortCountPerSec60=\"%.3f\" "
1364 "longBytesPer1Sec=\"%.3f\" longCountPer1Sec=\"%.3f\" longBytesPer10Sec=\"%.3f\" longCountPer10Sec=\"%.3f\" longBytesPer60Sec=\"%.3f\" longCountPer60Sec=\"%.3f\" "
1365 "/>\n",
1366 i->second.conID,
1367 GETIPADDRESSQUADPORT(i->second.location),
1368 i->second.count,
1369 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str(),
1370 svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3,
1371 lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3
1372 );
1373 }
1374 else {
1375 if (webSocketJSON.length())
1376 webSocketJSON += ",";
1377 webSocketJSON += utils::StringFormat("{\"id\":%llu, \"location\":\"%u.%u.%u.%u:%u\", \"count\":%u, \"uptime\":\"%s\", "
1378 " \"shortBytesPerSec1\": %.3f, \"shortCountPerSec1\": %.3f, \"shortBytesPerSec10\": %.3f, \"shortCountPerSec10\": %.3f, \"shortBytesPerSec60\": %.3f, \"shortCountPerSec60\": %.3f, "
1379 " \"longBytesPer1Sec\": %.3f, \"longCountPer1Sec\": %.3f, \"longBytesPer10Sec\": %.3f, \"longCountPer10Sec\": %.3f, \"longBytesPer60Sec\": %.3f, \"longCountPer60Sec\": %.3f "
1380 "}\n",
1381 i->second.conID,
1382 GETIPADDRESSQUADPORT(i->second.location),
1383 i->second.count,
1384 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str(),
1385 svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3,
1386 lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3
1387 );
1388 }
1389 webSocketCount++;
1390 }
1391 }
1392 shortAvgStats.getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1393 longAvgStats.getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1394 if (replyXML)
1395 xml = utils::StringFormat("<status uptime=\"%s\" version=\"%s\" executors=\"%u\" clients=\"%u\" webclients=\"%u\" websockets=\"%u\" maxqueuesize=\"%u\" minqueuesize=\"%u\" "
1396 "shortBytesPerSec1=\"%.3f\" shortCountPerSec1=\"%.3f\" shortBytesPerSec10=\"%.3f\" shortCountPerSec10=\"%.3f\" shortBytesPerSec60=\"%.3f\" shortCountPerSec60=\"%.3f\" "
1397 "longBytesPer1Sec=\"%.3f\" longCountPer1Sec=\"%.3f\" longBytesPer10Sec=\"%.3f\" longCountPer10Sec=\"%.3f\" longBytesPer60Sec=\"%.3f\" longCountPer60Sec=\"%.3f\" "
1398 ">\n%s</status>\n",
1399 PrintTimeDifString(GetTimeAge(systemStartTime)).c_str(), versionString.c_str(), execCount, clientCount, webClientCount,
1401 svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3,
1402 lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3,
1403 subXML.c_str());
1404 else
1405 json = utils::StringFormat("{\"uptime\":\"%s\", \"version\":\"%s\", \"executorcount\":%u, \"clientcount\":%u, \"webclientcount\":%u, \"websocketcount\":%u, \"maxqueuesize\":%u, \"minqueuesize\":%u, "
1406 "\"shortBytesPerSec1\": %.3f, \"shortCountPerSec1\": %.3f, \"shortBytesPerSec10\": %.3f, \"shortCountPerSec10\": %.3f, \"shortBytesPerSec60\": %.3f, \"shortCountPerSec60\": %.3f, "
1407 "\"longBytesPer1Sec\": %.3f, \"longCountPer1Sec\": %.3f, \"longBytesPer10Sec\": %.3f, \"longCountPer10Sec\": %.3f, \"longBytesPer60Sec\": %.3f, \"longCountPer60Sec\": %.3f, "
1408 "\"executors\":[%s], \"clients\":[%s], \"webclients\":[%s], \"websockets\":[%s]}\n",
1409 PrintTimeDifString(GetTimeAge(systemStartTime)).c_str(), versionString.c_str(), execCount, clientCount, webClientCount, webSocketCount,
1411 svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3,
1412 lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3,
1413 execJSON.c_str(), clientJSON.c_str(), webClientJSON.c_str(), webSocketJSON.c_str());
1414 }
1415 else if (((stricmp(apiName, "calls") == 0)) || utils::TextStartsWith(apiName, "calls?")) {
1416 uint32 count = 0;
1417 uint32 maxCount = utils::Ascii2Uint32(req->getParameter("maxcount"));
1418 if (!maxCount) maxCount = 100;
1419 uint32 accountID = utils::Ascii2Uint32(req->getParameter("accountid"));
1420 uint32 clientID = utils::Ascii2Uint32(req->getParameter("clientid"));
1421 uint32 port = utils::Ascii2Uint32(req->getParameter("port"));
1422 uint32 minduration = utils::Ascii2Uint32(req->getParameter("minduration"));
1423 uint32 maxduration = utils::Ascii2Uint32(req->getParameter("maxduration"));
1424 uint32 webonly = utils::Ascii2Uint32(req->getParameter("webonly"));
1425 uint32 messageonly = utils::Ascii2Uint32(req->getParameter("messageonly"));
1426 const char* status = req->getParameter("status");
1427 if (!replyXML) {
1428 if (callLogMutex.enter(100)) {
1429 for (ic = callLog.rbegin(), ec = callLog.rend(); ic != ec; ic++) {
1430 if (accountID && ((*ic)->accountID != accountID)) continue;
1431 if (clientID && ((*ic)->clientID != clientID)) continue;
1432 if (port && ((*ic)->port != port)) continue;
1433 if (minduration && ( ((*ic)->endTime - (*ic)->startTime) < (minduration * 1000))) continue;
1434 if (maxduration && (((*ic)->endTime - (*ic)->startTime) > (maxduration * 1000))) continue;
1435 if (webonly && ((*ic)->conType != 2)) continue;
1436 if (messageonly && ((*ic)->conType != 1)) continue;
1437 if (status && (!utils::stristr((*ic)->status.c_str(), status))) continue;
1438
1439 if ((json = (*ic)->toJSON()).length()) {
1440 if (execJSON.length())
1441 execJSON += ",";
1442 execJSON += json;
1443 }
1444 if (++count > maxCount) break;
1445 }
1446 callLogMutex.leave();
1447 }
1448 json = utils::StringFormat(
1449 "{\"uptime\":\"%s\", \"version\":\"%s\", \"executorcount\":%u, \"clientcount\":%u, \"webclientcount\":%u, \"websocketcount\":%u,\n"
1450 "\"count\":%u, \"calllog\": [%s]}\n",
1451 PrintTimeDifString(GetTimeAge(systemStartTime)).c_str(), versionString.c_str(), execCount, clientCount, webClientCount, webSocketCount,
1452 count, execJSON.c_str());
1453 }
1454 }
1455 else if (((stricmp(apiName, "throughput") == 0)) || utils::TextStartsWith(apiName, "throughput?")) {
1456 if (replyXML)
1457 xml = utils::StringFormat("<throughput><short>%s</short><long>%s</long></throughput>",
1458 shortAvgStats.getPerfXML().c_str(), longAvgStats.getPerfXML().c_str());
1459 else
1460 json = utils::StringFormat("{\"shortthroughput\": %s, \"longthroughput\": %s }",
1461 shortAvgStats.getPerfJSON().c_str(), longAvgStats.getPerfJSON().c_str());
1462 }
1463 else if (((stricmp(apiName, "available") == 0)) || utils::TextStartsWith(apiName, "available?")) {
1464 bool isAvailable = false;
1465 for (i = executors.begin(), e = executors.end(); i != e; i++) {
1466 if (!i->second.lastFailTime) {
1467 isAvailable = true;
1468 break;
1469 execCount++;
1470 }
1471 }
1472 if (isAvailable) {
1473 if (replyXML)
1474 xml = utils::StringFormat("<status available=\"yes\" />\n");
1475 else
1476 json = utils::StringFormat("{\"available\":\"yes\"}\n");
1477 }
1478 }
1479 else if (((stricmp(apiName, "stats") == 0)) || utils::TextStartsWith(apiName, "stats?")) {
1480 svalPerSec1 = scountPerSec1 = svalPerSec2 = scountPerSec2 = svalPerSec3 = scountPerSec3 =
1481 lvalPerSec1 = lcountPerSec1 = lvalPerSec2 = lcountPerSec2 = lvalPerSec3 = lcountPerSec3 = 0;
1482 for (i = executors.begin(), e = executors.end(); i != e; i++) {
1483 if (!i->second.lastFailTime) {
1484 //shortAvgStatsExec[i->second.conID].getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1485 //longAvgStatsExec[i->second.conID].getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1486 i->second.shortAvgStats.getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1487 i->second.longAvgStats.getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1488 if (replyXML) {
1489 subXML += utils::StringFormat("<executor id=\"%llu\" location=\"%u.%u.%u.%u:%u\" shortQueue=\"%u\" longQueue=\"%u\" count=\"%u\" uptime=\"%s\" "
1490 "shortBytesPerSec1=\"%.3f\" shortCountPerSec1=\"%.3f\" shortBytesPerSec10=\"%.3f\" shortCountPerSec10=\"%.3f\" shortBytesPerSec60=\"%.3f\" shortCountPerSec60=\"%.3f\" "
1491 "longBytesPer1Sec=\"%.3f\" longCountPer1Sec=\"%.3f\" longBytesPer10Sec=\"%.3f\" longCountPer10Sec=\"%.3f\" longBytesPer60Sec=\"%.3f\" longCountPer60Sec=\"%.3f\" />\n",
1492 i->second.conID,
1493 GETIPADDRESSQUADPORT(i->second.location),
1494 i->second.shortReqQueue.getCount(),
1495 i->second.longReqQueue.getCount(),
1496 i->second.count,
1497 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str(),
1498 svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3,
1499 lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3
1500 );
1501 }
1502 else {
1503 if (execJSON.length())
1504 execJSON += ",";
1505 //execJSON += utils::StringFormat("{\"id\":%llu, \"location\":\"%u.%u.%u.%u:%u\", \"shortQueue\":%u, \"longQueue\":%u, \"count\":%u, \"uptime\":\"%s\" }\n",
1506 // i->second.conID,
1507 // GETIPADDRESSQUADPORT(i->second.location),
1508 // i->second.shortReqQSize,
1509 // i->second.longReqQSize,
1510 // i->second.count,
1511 // PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str()
1512 // );
1513 execJSON += utils::StringFormat("{\"id\":%llu, \"location\":\"%u.%u.%u.%u:%u\", \"shortQueue\":%u, \"longQueue\":%u, \"count\":%u, \"uptime\":\"%s\", "
1514 "\"shortBytesPerSec1\": %.3f, \"shortCountPerSec1\": %.3f, \"shortBytesPerSec10\": %.3f, \"shortCountPerSec10\": %.3f, \"shortBytesPerSec60\": %.3f, \"shortCountPerSec60\": %.3f, "
1515 "\"longBytesPer1Sec\": %.3f, \"longCountPer1Sec\": %.3f, \"longBytesPer10Sec\": %.3f, \"longCountPer10Sec\": %.3f, \"longBytesPer60Sec\": %.3f, \"longCountPer60Sec\": %.3f }\n",
1516 i->second.conID,
1517 GETIPADDRESSQUADPORT(i->second.location),
1518 i->second.shortReqQueue.getCount(),
1519 i->second.longReqQueue.getCount(),
1520 i->second.count,
1521 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str(),
1522 svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3,
1523 lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3
1524 );
1525 }
1526 execCount++;
1527 }
1528 }
1529 for (i = clients.begin(), e = clients.end(); i != e; i++) {
1530 if (!i->second.lastFailTime) {
1531 //shortAvgStatsClients[i->second.conID].getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1532 //longAvgStatsClients[i->second.conID].getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1533 i->second.shortAvgStats.getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1534 i->second.longAvgStats.getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1535 if (replyXML) {
1536 subXML += utils::StringFormat("<client id=\"%llu\" location=\"%u.%u.%u.%u:%u\" count=\"%u\" uptime=\"%s\" "
1537 "shortBytesPerSec1=\"%.3f\" shortCountPerSec1=\"%.3f\" shortBytesPerSec10=\"%.3f\" shortCountPerSec10=\"%.3f\" shortBytesPerSec60=\"%.3f\" shortCountPerSec60=\"%.3f\" "
1538 "longBytesPer1Sec=\"%.3f\" longCountPer1Sec=\"%.3f\" longBytesPer10Sec=\"%.3f\" longCountPer10Sec=\"%.3f\" longBytesPer60Sec=\"%.3f\" longCountPer60Sec=\"%.3f\" />\n",
1539 i->second.conID,
1540 GETIPADDRESSQUADPORT(i->second.location),
1541 i->second.count,
1542 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str(),
1543 svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3,
1544 lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3
1545 );
1546 }
1547 else {
1548 if (clientJSON.length())
1549 clientJSON += ",";
1550 clientJSON += utils::StringFormat("{\"id\":%llu, \"location\":\"%u.%u.%u.%u:%u\", \"count\":%u, \"uptime\":\"%s\", "
1551 "\"shortBytesPerSec1\": %.3f, \"shortCountPerSec1\": %.3f, \"shortBytesPerSec10\": %.3f, \"shortCountPerSec10\": %.3f, \"shortBytesPerSec60\": %.3f, \"shortCountPerSec60\": %.3f, "
1552 "\"longBytesPer1Sec\": %.3f, \"longCountPer1Sec\": %.3f, \"longBytesPer10Sec\": %.3f, \"longCountPer10Sec\": %.3f, \"longBytesPer60Sec\": %.3f, \"longCountPer60Sec\": %.3f }\n",
1553 i->second.conID,
1554 GETIPADDRESSQUADPORT(i->second.location),
1555 i->second.count,
1556 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str(),
1557 svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3,
1558 lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3
1559 );
1560 }
1561 clientCount++;
1562 }
1563 }
1564 for (i = webClients.begin(), e = webClients.end(); i != e; i++) {
1565 if (!i->second.lastFailTime) {
1566 //shortAvgStatsWeb[i->second.conID].getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1567 //longAvgStatsWeb[i->second.conID].getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1568 i->second.shortAvgStats.getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1569 i->second.longAvgStats.getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1570 if (replyXML) {
1571 subXML += utils::StringFormat("<webclient id=\"%llu\" location=\"%u.%u.%u.%u:%u\" count=\"%u\" uptime=\"%s\" "
1572 "shortBytesPerSec1=\"%.3f\" shortCountPerSec1=\"%.3f\" shortBytesPerSec10=\"%.3f\" shortCountPerSec10=\"%.3f\" shortBytesPerSec60=\"%.3f\" shortCountPerSec60=\"%.3f\" "
1573 "longBytesPer1Sec=\"%.3f\" longCountPer1Sec=\"%.3f\" longBytesPer10Sec=\"%.3f\" longCountPer10Sec=\"%.3f\" longBytesPer60Sec=\"%.3f\" longCountPer60Sec=\"%.3f\" />\n",
1574 i->second.conID,
1575 GETIPADDRESSQUADPORT(i->second.location),
1576 i->second.count,
1577 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str(),
1578 svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3,
1579 lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3
1580 );
1581 }
1582 else {
1583 if (webClientJSON.length())
1584 webClientJSON += ",";
1585 webClientJSON += utils::StringFormat("{\"id\":%llu, \"location\":\"%u.%u.%u.%u:%u\", \"count\":%u, \"uptime\":\"%s\", "
1586 "\"shortBytesPerSec1\": %.3f, \"shortCountPerSec1\": %.3f, \"shortBytesPerSec10\": %.3f, \"shortCountPerSec10\": %.3f, \"shortBytesPerSec60\": %.3f, \"shortCountPerSec60\": %.3f, "
1587 "\"longBytesPer1Sec\": %.3f, \"longCountPer1Sec\": %.3f, \"longBytesPer10Sec\": %.3f, \"longCountPer10Sec\": %.3f, \"longBytesPer60Sec\": %.3f, \"longCountPer60Sec\": %.3f }\n",
1588 i->second.conID,
1589 GETIPADDRESSQUADPORT(i->second.location),
1590 i->second.count,
1591 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str(),
1592 svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3,
1593 lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3
1594 );
1595 }
1596 webClientCount++;
1597 }
1598 }
1599 for (i = webSockets.begin(), e = webSockets.end(); i != e; i++) {
1600 if (!i->second.lastFailTime) {
1601 //shortAvgStatsWeb[i->second.conID].getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1602 //longAvgStatsWeb[i->second.conID].getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1603 i->second.shortAvgStats.getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1604 i->second.longAvgStats.getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1605 if (replyXML) {
1606 subXML += utils::StringFormat("<websocket id=\"%llu\" location=\"%u.%u.%u.%u:%u\" count=\"%u\" uptime=\"%s\" "
1607 "shortBytesPerSec1=\"%.3f\" shortCountPerSec1=\"%.3f\" shortBytesPerSec10=\"%.3f\" shortCountPerSec10=\"%.3f\" shortBytesPerSec60=\"%.3f\" shortCountPerSec60=\"%.3f\" "
1608 "longBytesPer1Sec=\"%.3f\" longCountPer1Sec=\"%.3f\" longBytesPer10Sec=\"%.3f\" longCountPer10Sec=\"%.3f\" longBytesPer60Sec=\"%.3f\" longCountPer60Sec=\"%.3f\" />\n",
1609 i->second.conID,
1610 GETIPADDRESSQUADPORT(i->second.location),
1611 i->second.count,
1612 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str(),
1613 svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3,
1614 lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3
1615 );
1616 }
1617 else {
1618 if (webSocketJSON.length())
1619 webSocketJSON += ",";
1620 webSocketJSON += utils::StringFormat("{\"id\":%llu, \"location\":\"%u.%u.%u.%u:%u\", \"count\":%u, \"uptime\":\"%s\", "
1621 "\"shortBytesPerSec1\": %.3f, \"shortCountPerSec1\": %.3f, \"shortBytesPerSec10\": %.3f, \"shortCountPerSec10\": %.3f, \"shortBytesPerSec60\": %.3f, \"shortCountPerSec60\": %.3f, "
1622 "\"longBytesPer1Sec\": %.3f, \"longCountPer1Sec\": %.3f, \"longBytesPer10Sec\": %.3f, \"longCountPer10Sec\": %.3f, \"longBytesPer60Sec\": %.3f, \"longCountPer60Sec\": %.3f }\n",
1623 i->second.conID,
1624 GETIPADDRESSQUADPORT(i->second.location),
1625 i->second.count,
1626 PrintTimeDifString(GetTimeAge(i->second.lastConTime)).c_str(),
1627 svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3,
1628 lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3
1629 );
1630 }
1631 webSocketCount++;
1632 }
1633 }
1634 shortAvgStats.getThroughputMulti(ms1, ms2, ms3, svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3, now);
1635 longAvgStats.getThroughputMulti(ms1, ms2, ms3, lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3, now);
1636 if (replyXML)
1637 xml = utils::StringFormat("<status executors=\"%u\" clients=\"%u\" webclients=\"%u\" websockets=\"%u\" "
1638 "shortBytesPerSec1=\"%.3f\" shortCountPerSec1=\"%.3f\" shortBytesPerSec10=\"%.3f\" shortCountPerSec10=\"%.3f\" shortBytesPerSec60=\"%.3f\" shortCountPerSec60=\"%.3f\" "
1639 "longBytesPer1Sec=\"%.3f\" longCountPer1Sec=\"%.3f\" longBytesPer10Sec=\"%.3f\" longCountPer10Sec=\"%.3f\" longBytesPer60Sec=\"%.3f\" longCountPer60Sec=\"%.3f\" "
1640 ">\n%s</status>\n", execCount, clientCount, webClientCount, webSocketCount,
1641 svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3,
1642 lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3,
1643 subXML.c_str());
1644 else
1645 json = utils::StringFormat("{\"executorcount\":%u, \"clientcount\":%u, \"webclientcount\":%u, \"websocketcount\":%u, "
1646 "\"shortBytesPerSec1\": %.3f, \"shortCountPerSec1\": %.3f, \"shortBytesPerSec10\": %.3f, \"shortCountPerSec10\": %.3f, \"shortBytesPerSec60\": %.3f, \"shortCountPerSec60\": %.3f, "
1647 "\"longBytesPer1Sec\": %.3f, \"longCountPer1Sec\": %.3f, \"longBytesPer10Sec\": %.3f, \"longCountPer10Sec\": %.3f, \"longBytesPer60Sec\": %.3f, \"longCountPer60Sec\": %.3f, "
1648 "\"executors\":[%s], \"clients\":[%s], \"webclients\":[%s], \"websockets\":[%s]}\n",
1649 execCount, clientCount, webClientCount, webSocketCount,
1650 svalPerSec1, scountPerSec1, svalPerSec2, scountPerSec2, svalPerSec3, scountPerSec3,
1651 lvalPerSec1, lcountPerSec1, lvalPerSec2, lcountPerSec2, lvalPerSec3, lcountPerSec3,
1652 execJSON.c_str(), clientJSON.c_str(), webClientJSON.c_str(), webSocketJSON.c_str());
1653 }
1654 else if (stricmp(apiName, "sizeechotest") == 0) {
1655 // size = contentlength - 40 (as we add about 40 chars of XML below)
1656 uint32 size = (req->contentLength < 50) ? 10 : req->contentLength - 40;
1657 uint32 n;
1658 char* echocontent = new char[size+1];
1659 for (n=0; n<size; n++)
1660 echocontent[n] = 48 + (n%10);
1661 echocontent[n] = 0;
1662 if (replyXML)
1663 xml = utils::StringFormat("<echosizetest size=\"%u\">%s</echosizetest>", size, echocontent);
1664 else
1665 json = utils::StringFormat("{\"size\":%u, \"content\":\"%s\"}\n", size, echocontent);
1666 delete [] echocontent;
1667 }
1668 else if (utils::TextStartsWith(apiName, "restart?", false)) {
1669 uint64 idNum = 0;
1670 const char* id = req->getParameter("id");
1671 if (id && (stricmp(id, "all") == 0)) {
1672 uint32 successCount = 0;
1673 uint32 failedCount = 0;
1674 std::map<uint64, RequestConnection>::iterator i, e;
1675 for (i = executors.begin(), e = executors.end(); i != e; i++) {
1676 if (sendRestartToExecutor(i->first))
1677 successCount++;
1678 else
1679 failedCount++;
1680 }
1681 if (!successCount) {
1682 if (replyXML)
1683 xml = utils::StringFormat("<error text=\"None of the %u executors were restarted\" />", failedCount);
1684 else
1685 json = utils::StringFormat("{\"error\":\"None of the %u executors were restarted\"}\n", failedCount);
1686 }
1687 else {
1688 if (replyXML)
1689 xml = utils::StringFormat("<success text=\"Restart sent to %u executor successfully (%u failed sending)\" />", successCount, failedCount);
1690 else
1691 json = utils::StringFormat("{\"success\":\"Restart sent to %u executor successfully (%u failed sending)\"}\n", successCount, failedCount);
1692 }
1693 }
1694 else if (id && ((idNum = utils::Ascii2Uint64(id)) > 0)) {
1695 if (sendRestartToExecutor(idNum)) {
1696 if (replyXML)
1697 xml = utils::StringFormat("<success text=\"Restart sent to executor %llu successfully\" />", idNum);
1698 else
1699 json = utils::StringFormat("{\"success\":\"Restart sent to executor %llu successfully\"}\n", idNum);
1700 }
1701 else {
1702 if (replyXML)
1703 xml = utils::StringFormat("<error text=\"Failed to send restart to executor %llu\" />", idNum);
1704 else
1705 json = utils::StringFormat("{\"error\":\"Failed to send restart to executor %llu\"}\n", idNum);
1706 }
1707 }
1708 else {
1709 if (replyXML)
1710 xml = utils::StringFormat("<error text=\"No valid id provided\" />");
1711 else
1712 json = utils::StringFormat("{\"error\":\"No valid id provided\"}\n");
1713 }
1714 }
1715 else if (strlen(apiName)) {
1716 if (rootdir.length()) {
1717 if (reply->createFromFile(GetTimeNow(), webServerName.c_str(), req->ifModifiedSince, true, cacheFiles, (rootdir + apiName).c_str())) {
1718 addToCallLog(++callLogCount, 2, 3, false, req->source, 0, 0, 0, req->endReceiveTime, GetTimeNow(), 0, GetTimeAge(now), req->type, req->getSize(), reply->getSize(), "Success",
1719 utils::StringFormat("Internal: %s", apiName).c_str(), req->getHeaderEntry("User-Agent"), "");
1720 channel->sendHTTPReply(reply, conid);
1721 delete(reply);
1722 return true;
1723 }
1724 else {
1725 if (replyXML)
1726 xml = utils::StringFormat("<error code=\"-1\" text=\"File %s not found in rootdir\" />", apiName);
1727 else
1728 json = utils::StringFormat("{\"errorcode\":-1, \"text\":\"File %s not found in rootdir\"}\n", apiName);
1729 }
1730 }
1731 else {
1732 if (replyXML)
1733 xml = utils::StringFormat("<error code=\"-1\" text=\"Cannot generate %s, no rootdir configured\" />", apiName);
1734 else
1735 json = utils::StringFormat("{\"errorcode\":-1, \"text\":\"Cannot generate %s, no rootdir configured\"}\n", apiName);
1736 }
1737 }
1738 else {
1739 if (rootdir.length()) {
1740 if (reply->createFromFile(GetTimeNow(), webServerName.c_str(), req->ifModifiedSince, true, cacheFiles, (rootdir + indexFilename).c_str())) {
1741 channel->sendHTTPReply(reply, conid);
1742 addToCallLog(++callLogCount, 2, 3, false, req->source, 0, 0, 0, req->endReceiveTime, GetTimeNow(), 0, GetTimeAge(now), req->type, req->getSize(), reply->getSize(), "Success",
1743 utils::StringFormat("Internal: %s", apiName).c_str(), req->getHeaderEntry("User-Agent"), "");
1744 delete(reply);
1745 return true;
1746 }
1747 else {
1748 if (replyXML)
1749 xml = utils::StringFormat("<error code=\"-1\" text=\"No default file found in rootdir\" />");
1750 else
1751 json = utils::StringFormat("{\"errorcode\":-1, \"text\":\"No default file found in rootdir\"}\n");
1752 }
1753 }
1754 else {
1755 if (replyXML)
1756 xml = utils::StringFormat("<error code=\"-1\" text=\"Cannot generate %s, no rootdir configured\" />", apiName);
1757 else
1758 json = utils::StringFormat("{\"errorcode\":-1, \"text\":\"Cannot generate %s, no rootdir configured\"}\n", apiName);
1759 }
1760 }
1761
1762 if (!xml.length() && !json.length()) {
1763 reply->createPage(HTTP_SERVICE_NOT_AVAILABLE, GetTimeNow(), webServerName.c_str(), 0, true, false, "text/plain", "");
1764 addToCallLog(++callLogCount, 2, 3, false, req->source, 0, 0, 0, req->endReceiveTime, GetTimeNow(), 0, GetTimeAge(now), req->type, req->getSize(), reply->getSize(), "Internal Service Not Available",
1765 utils::StringFormat("Internal: %s", apiName).c_str(), req->getHeaderEntry("User-Agent"), "");
1766 }
1767 else if (replyXML) {
1768 reply->createPage(HTTP_OK, GetTimeNow(), webServerName.c_str(), 0, true, false, "text/xml", xml.c_str());
1769 addToCallLog(++callLogCount, 2, 3, false, req->source, 0, 0, 0, req->endReceiveTime, GetTimeNow(), 0, GetTimeAge(now), req->type, req->getSize(), reply->getSize(), "Success",
1770 utils::StringFormat("Internal: %s", apiName).c_str(), req->getHeaderEntry("User-Agent"), "");
1771 }
1772 else {
1773 reply->createPage(HTTP_OK, GetTimeNow(), webServerName.c_str(), 0, true, false, "application/json", json.c_str());
1774 addToCallLog(++callLogCount, 2, 3, false, req->source, 0, 0, 0, req->endReceiveTime, GetTimeNow(), 0, GetTimeAge(now), req->type, req->getSize(), reply->getSize(), "Success",
1775 utils::StringFormat("Internal: %s", apiName).c_str(), req->getHeaderEntry("User-Agent"), "");
1776 }
1777
1778 channel->sendHTTPReply(reply, conid);
1779
1780 delete(reply);
1781 return true;
1782}
1783
1785 longReqLimit = limit;
1786 return true;
1787}
1788
1789//bool shortQIsSmaller(const RequestConnection& r1, const RequestConnection& r2) {
1790// return (r1.shortReqQueue.getCount() < r2.shortReqQueue.getCount());
1791//}
1792//
1793//bool longQIsSmaller(const RequestConnection& r1, const RequestConnection& r2) {
1794// return (r1.longReqQueue.getCount() < r2.longReqQueue.getCount());
1795//}
1796
1797
1798uint64 RequestGateway::getBestExecutorID(std::string requestString, uint32 reqSize, bool& isLongReq) {
1799 // Load-balancing pick. First classify the request: "long" when the raw
1800 // request size exceeds longReqLimit (if set) or when the request string
1801 // contains any registered long-request name (case-insensitive substring).
1802 // Then choose among executors that are currently connected (lastConTime
1803 // set, no recorded failure) the one with the shortest queue. Note: the
1804 // comparison always uses longReqQueue.getCount(), even for short requests
1805 // — short-queue depth and the latency MovingAverages tracked per
1806 // connection do not currently influence the choice. Returns 0 when no
1807 // healthy executor exists (callers then fail the request as TOOBUSY /
1808 // server-unavailable).
1809 isLongReq = false;
1810 if (longReqLimit)
1811 isLongReq = (reqSize >= longReqLimit);
1812 if (!isLongReq) {
1813 std::vector<std::string>::iterator il = longReqNames.begin(), el = longReqNames.end();
1814 while (il != el) {
1815 if (utils::stristr(requestString.c_str(), il->c_str())) {
1816 isLongReq = true;
1817 break;
1818 }
1819 il++;
1820 }
1821 }
1822
1823 uint64 bestID = 0;
1824 uint32 bestQSize = MAXVALUINT32;
1825 std::map<uint64, RequestConnection>::iterator i, e;
1826 for (i = executors.begin(), e = executors.end(); i != e; i++) {
1827 if (i->second.lastConTime && !i->second.lastFailTime) {
1828 if (i->second.longReqQueue.getCount() < bestQSize) {
1829 bestQSize = i->second.longReqQueue.getCount();
1830 bestID = i->first;
1831 }
1832 }
1833 }
1834// printf("\n ----- Best Executor is ID: %llu (Q: %u)\n\n", bestID, bestQSize);
1835 return bestID;
1836}
1837
1838
1840 uint64 id,
1841 uint8 conType,
1842 uint8 messageType,
1843 bool longRequest,
1844 uint64 source,
1845 uint16 port,
1846 uint64 executorID,
1847 uint64 clientID,
1848 uint64 startTime,
1849 uint64 endTime,
1850 uint64 queueTime,
1851 uint64 processTime,
1852 uint8 requestType,
1853 uint32 requestSize,
1854 uint32 replySize,
1855 const char* status,
1856 const char* requestPath,
1857 const char* agent,
1858 const char* output
1859) {
1860 if (!callLogMutex.enter(100)) return false;
1861
1862 // NULL-safe: callers pass getString()/getHeaderEntry() results which can be
1863 // NULL (e.g. a DataMessage request has no "User-Agent"). Constructing a
1864 // std::string from NULL would crash, so coerce NULLs to empty here.
1865 std::string statusS = status ? status : "";
1866 std::string requestPathS = requestPath ? requestPath : "";
1867 std::string agentS = agent ? agent : "";
1868 std::string outputS = output ? output : "";
1869
1870 std::basic_string<char>::size_type t = requestPathS.find("?_=");
1871 if (t != std::string::npos)
1872 requestPathS.erase(t);
1873 t = requestPathS.find("&_=");
1874 if (t != std::string::npos)
1875 requestPathS.erase(t);
1876
1877 CallLogEntry* entry = new CallLogEntry(id);
1878 entry->conType = conType;
1879 entry->messageType = messageType;
1880 entry->longRequest = longRequest;
1881 entry->source = source;
1882 entry->port = port;
1883 entry->executorID = executorID;
1884 entry->clientID = clientID;
1885 entry->startTime = startTime;
1886 entry->endTime = endTime;
1887 entry->queueTime = queueTime;
1888 entry->processTime = processTime;
1889 entry->requestType = requestType;
1890 entry->requestSize = requestSize;
1891 entry->replySize = replySize;
1892 entry->status = statusS;
1893 entry->requestPath = requestPathS;
1894 entry->agent = agentS;
1895 entry->output = outputS;
1896
1897 callLog.push_back(entry);
1898 while (callLog.size() > callLogMax) {
1899 delete callLog.front();
1900 callLog.pop_front();
1901 }
1902
1903 callLogMutex.leave();
1904 return true;
1905}
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917//std::list<RequestConnection> RequestGateway::getTopExecutors(std::string requestString, uint32 reqSize, bool& isLongReq) {
1918// isLongReq = false;
1919// if (longReqLimit)
1920// isLongReq = (reqSize >= longReqLimit);
1921// if (!isLongReq) {
1922// std::vector<std::string>::iterator il = longReqNames.begin(), el = longReqNames.end();
1923// while (il != el) {
1924// if (utils::stristr(requestString.c_str(), il->c_str())) {
1925// isLongReq = true;
1926// break;
1927// }
1928// il++;
1929// }
1930// }
1931// std::list<RequestConnection> executorsList;
1932// std::map<uint64,RequestConnection>::iterator i,e;
1933// for (i=executors.begin(), e=executors.end(); i!=e; i++)
1934// executorsList.push_back(i->second);
1935// if (isLongReq) {
1936// executorsList.sort(longQIsSmaller);
1937// //printf("--- Long EXELIST [%u] ---\n", executorsList.size());
1938// }
1939// else {
1940// executorsList.sort(shortQIsSmaller);
1941// //std::sort(executorsList.begin(), executorsList.end(), shortQIsSmaller);
1942// //printf("--- Short EXELIST [%u] ---\n", executorsList.size());
1943// }
1944//
1945// //uint32 n = 0;
1946// //std::list<RequestConnection>::iterator ii,ee;
1947// //for (ii=executorsList.begin(), ee=executorsList.end(); ii!=ee; ii++) {
1948// // printf(" [%u] [%llu] SQ: %u LQ: %u\n", n, (*ii).conID, (*ii).shortReqQSize, (*ii).longReqQSize);
1949// // fflush(stdout);
1950// //}
1951//
1952// return executorsList;
1953//}
1954
1956 std::map<uint64, RequestConnection>::iterator i = executors.find(id);
1957 if (i == executors.end())
1958 return false;
1959
1960 DataMessage* msg = new DataMessage();
1961 msg->setString("REQUEST", "__RESTART__");
1962
1963 //printf("Sending restart message to con id %llu...\n\n", i->second.conID);
1964 bool res = channel->sendMessage(msg, i->second.conID);
1965 delete msg;
1966 return res;
1967}
1968
1969
1970//bool RequestGateway::sendToExecutor(RequestReply* reply) {
1971 //std::list<RequestConnection> topExecutors;
1972 //std::list<RequestConnection>::iterator i, e;
1973 //requestMap[reply->gatewayRef] = reply;
1974 //DataMessage *replyMsg;
1975
1976 //DataMessage* reqMsg = reply->peekRequestMessage();
1977 //if (!reqMsg) {
1978 // replyMsg = new DataMessage();
1979 // replyMsg->setReference(reply->clientRef);
1980 // if (replyXML)
1981 // replyMsg->setString("XML", "<error>No input data available</error>");
1982 // else
1983 // replyMsg->setString("JSON", "{\"error\": \"No input data available\"}\n");
1984 // replyQ.add(replyMsg);
1985 // return false;
1986 //}
1987 //const char* reqStr = reqMsg->getString("URI");
1988 //if (!reqStr)
1989 // reqStr = reqMsg->getString("REQUEST");
1990 //if (!reqStr) {
1991 // replyMsg = new DataMessage();
1992 // replyMsg->setReference(reply->clientRef);
1993 // if (replyXML)
1994 // replyMsg->setString("XML", "<error>No request data available</error>");
1995 // else
1996 // replyMsg->setString("JSON", "{\"error\": \"No request data available\"}\n");
1997 // replyQ.add(replyMsg);
1998 // return false;
1999 //}
2000
2001 //topExecutors = getTopExecutors(reqStr, reqMsg->getSize(), reply->isLongReq);
2002
2003 //if (!topExecutors.size()) {
2004 // LogPrint(0, LOG_SYSTEM, 0, "No executors found!");
2005 // reply->setStatus(FAILED);
2006 // replyMsg = new DataMessage();
2007 // replyMsg->setReference(reply->clientRef);
2008 // if (replyXML)
2009 // replyMsg->setString("XML", "<error>No processors available</error>");
2010 // else
2011 // replyMsg->setString("JSON", "{\"error\": \"No processors available\"}\n");
2012 // replyQ.add(replyMsg);
2013 // return false;
2014 //}
2015
2016 //bool wasSent = false;
2017 //for (i=topExecutors.begin(), e=topExecutors.end(); i!=e; i++) {
2018 // uint64 t = GetTimeNow();
2019 // if (channel->sendMessage(reply->peekRequestMessage(), (*i).conID)) {
2020 // //printf("[[[%.3fms]]]\n", GetTimeAge(t)/1000.0);
2021 // executors[(*i).conID].count++;
2022 // reply->processor = (*i).conID;
2023 // //printf("--- GW -> EX[%llu] ---\n", (*i).conID);
2024 // //fflush(stdout);
2025 // //LogPrint(0, LOG_SYSTEM, 0, "Gateway sent request to Executor");
2026 // wasSent = true;
2027 // break;
2028 // }
2029 //}
2030 //if (wasSent) {
2031 // reply->setStatus(SENT);
2032 // execSentCount++;
2033 //}
2034 //else {
2035 // reply->setStatus(SERVERERROR);
2036 // // #########
2037 // // reply to client: server error
2038 // return false;
2039 //}
2040 //return true;
2041//}
2042
2044 uint64 gwRef = msg->getReference();
2045 uint64 now = GetTimeNow();
2046
2047 if (!mutex.enter(1000, __FUNCTION__)) {
2048 LogPrint(0, LOG_SYSTEM, 0, "Error locking Gateway Client Reply mutex");
2049 delete(msg);
2050 return true;
2051 }
2052
2053 std::map<uint64,RequestReply*>::iterator i = requestMap.find(gwRef), e;
2054 std::map<uint64,RequestConnection>::iterator con;
2055
2056 if (i == requestMap.end()) {
2057 // #############
2058 LogPrint(0, LOG_SYSTEM, 0, "Gateway couldn't find request to reply to, ref: %llu", gwRef);
2059 delete(msg);
2060 //for (i = requestMap.begin(), e = requestMap.end(); i != e; i++) {
2061 // printf("[%llu] (%llu->%llu) %s\n", i->second->gatewayRef, i->second->origin, i->second->processor, i->second->getStatusText().c_str());
2062 //}
2063 mutex.leave();
2064 return false;
2065 }
2066 RequestReply* reply = i->second;
2067
2068 // Add Gateway stats
2069 //MovingAverage* avg;
2070 uint32 reqSize = reply->peekRequestMessage()->getSize();
2071 if (reply->isLongReq) {
2072 longAvgStats.add(reqSize);
2073 //avg = &(longAvgStatsExec[reply->processor]);
2074 //avg->add(reqSize);
2075 if (reply->processor) {
2076 executors[reply->processor].longReqQProcessingSize--;
2077 executors[reply->processor].longReqQueue.completeRequest(reply);
2078 executors[reply->processor].longAvgStats.add(reqSize);
2079 }
2080 }
2081 else {
2082 shortAvgStats.add(reqSize);
2083 //avg = &(shortAvgStatsExec[reply->processor]);
2084 //avg->add(reqSize);
2085 if (reply->processor) {
2086 executors[reply->processor].shortReqQueue.completeRequest(reply);
2087 executors[reply->processor].shortAvgStats.add(reqSize);
2088 }
2089 }
2090
2091 if ( (con=webClients.find(reply->origin)) != webClients.end()) {
2092 if (reply->isLongReq)
2093 con->second.longAvgStats.add(reqSize);
2094 else
2095 con->second.shortAvgStats.add(reqSize);
2096 // avg = &(longAvgStatsWeb[reply->origin]);
2097 // avg = &(shortAvgStatsWeb[reply->origin]);
2098 //avg->add(reqSize);
2099 HTTPReply* httpReply = new HTTPReply();
2100 const char* xml, *html, *text, *data, *mime, *json, *cache, *options, *origin;
2101 int64 resultCode = 0;
2102 msg->getInt("ResultCode", resultCode);
2103 if (!resultCode) resultCode = HTTP_OK;
2104 cache = msg->getString("CACHE");
2105 bool cacheable = (cache && (utils::stristr(cache, "yes") == cache));
2106 uint32 size;
2107 // ########## Add reference and reply to HTTP reply #######
2108 if (xml = msg->getString("XML"))
2109 httpReply->createPage((uint8)resultCode, GetTimeNow(), webServerName.c_str(), 0, true, cacheable, "text/xml", xml);
2110 else if (json = msg->getString("JSON"))
2111 httpReply->createPage((uint8)resultCode, GetTimeNow(), webServerName.c_str(), 0, true, cacheable, "application/json", json);
2112 else if (html = msg->getString("HTML"))
2113 httpReply->createPage((uint8)resultCode, GetTimeNow(), webServerName.c_str(), 0, true, cacheable, "text/html", html);
2114 else if (text = msg->getString("TEXT"))
2115 httpReply->createPage((uint8)resultCode, GetTimeNow(), webServerName.c_str(), 0, true, cacheable, "text/text", html);
2116 else if ( (data = msg->getData("BINARY", size)) && size && (mime = msg->getString("MIMETYPE")) )
2117 httpReply->createPage((uint8)resultCode, GetTimeNow(), webServerName.c_str(), 0, true, cacheable, mime, data, size);
2118 else if ( (options = msg->getString("OPTIONS")) && (origin = msg->getString("ORIGIN")) )
2119 httpReply->createOptionsResponse((uint8)resultCode, GetTimeNow(), webServerName.c_str(), 0, origin, options);
2120 else
2121 httpReply->createErrorPage(HTTP_SERVER_NOREPLY, GetTimeNow(), webServerName.c_str(), true);
2122
2123 if (channel->sendHTTPReply(httpReply, reply->origin)) {
2124
2125 addToCallLog(++callLogCount, 2, 3, reply->isLongReq, reply->peekRequestMessage()->getTime("SOURCE"), 0, reply->processor, 0, reply->startTime, now, 0, GetTimeAge(reply->startTime), (uint8)reply->peekRequestMessage()->getInt("HTTP_OPERATION"), reply->peekRequestMessage()->getSize(), msg->getSize(), "Success",
2126 reply->peekRequestMessage()->getString("URI"), reply->peekRequestMessage()->getString("User-Agent"), "");
2127
2128 LogPrint(0, LOG_SYSTEM, 3, "Replied to %s request %llu from Executor %llu via HTTP...", reply->isLongReq ? "LONG" : "SHORT", reply->gatewayRef, reply->processor);
2129 con->second.count++;
2130 delete(httpReply);
2131 delete(msg);
2132 requestMap.erase(i);
2133 delete(reply);
2134 mutex.leave();
2135 return true;
2136 }
2137 else {
2138
2139 addToCallLog(++callLogCount, 2, 3, reply->isLongReq, reply->peekRequestMessage()->getTime("SOURCE"), 0, reply->processor, 0, reply->startTime, now, 0, GetTimeAge(reply->startTime), (uint8)reply->peekRequestMessage()->getInt("HTTP_OPERATION"), reply->peekRequestMessage()->getSize(), msg->getSize(), "Failed to send reply",
2140 reply->peekRequestMessage()->getString("URI"), reply->peekRequestMessage()->getString("User-Agent"), "");
2141
2142 LogPrint(0, LOG_SYSTEM, 0, "Failed to reply to %s request %llu from Executor %llu via HTTP...", reply->isLongReq ? "LONG" : "SHORT", reply->gatewayRef, reply->processor);
2143 delete(httpReply);
2144 delete(msg);
2145 requestMap.erase(i);
2146 delete(reply);
2147 mutex.leave();
2148 return false;
2149 }
2150 }
2151 else if ((con = webSockets.find(reply->origin)) != webSockets.end()) {
2152 if (reply->isLongReq)
2153 con->second.longAvgStats.add(reqSize);
2154 else
2155 con->second.shortAvgStats.add(reqSize);
2156 // avg = &(longAvgStatsWeb[reply->origin]);
2157 // avg = &(shortAvgStatsWeb[reply->origin]);
2158 //avg->add(reqSize);
2159
2160 WebsocketData* wsData = new WebsocketData();
2161 JSONM* jm = new JSONM();
2162 int64 resultCode = 0;
2163 msg->getInt("ResultCode", resultCode);
2164 if (!resultCode) resultCode = HTTP_OK;
2165
2166 uint64 ref = reply->clientRef;
2167
2168 std::string subJSON = utils::StringFormat("\"resultcode\": \"%s\", \"requestid\": %llu",
2169 HTTP_Status[resultCode], ref);
2170 std::string json = AddToJSONRoot(msg->getString("JSON"), subJSON.c_str());
2171 jm->setJSON(json.c_str());
2172
2173 const char* data;
2174 const char* mime;
2175 uint32 size;
2176 if ((data = msg->getData("BINARY", size)) && size && (mime = msg->getString("MIMETYPE")))
2177 jm->addData("binary", data, size, mime);
2178 wsData->setData(wsData->BINARY, false, jm->jmData, jm->jmSize);
2179
2180 if (channel->sendWebsocketData(wsData, reply->origin)) {
2181
2182 addToCallLog(++callLogCount, 3, 3, reply->isLongReq, reply->peekRequestMessage()->getTime("SOURCE"), 0, reply->processor, 0, reply->startTime, now, 0, GetTimeAge(reply->startTime), (uint8)reply->peekRequestMessage()->getInt("HTTP_OPERATION"), reply->peekRequestMessage()->getSize(), msg->getSize(), "Success",
2183 reply->peekRequestMessage()->getString("URI"), reply->peekRequestMessage()->getString("User-Agent"), "");
2184
2185 LogPrint(0, LOG_SYSTEM, 3, "Replied to %s request %llu from Executor %llu via WebSocket...", reply->isLongReq ? "LONG" : "SHORT", reply->gatewayRef, reply->processor);
2186 con->second.count++;
2187 delete(wsData);
2188 delete(msg);
2189 requestMap.erase(i);
2190 delete(reply);
2191 mutex.leave();
2192 return true;
2193 }
2194 else {
2195
2196 addToCallLog(++callLogCount, 3, 3, reply->isLongReq, reply->peekRequestMessage()->getTime("SOURCE"), 0, reply->processor, 0, reply->startTime, now, 0, GetTimeAge(reply->startTime), (uint8)reply->peekRequestMessage()->getInt("HTTP_OPERATION"), reply->peekRequestMessage()->getSize(), msg->getSize(), "Failed to send reply",
2197 reply->peekRequestMessage()->getString("URI"), reply->peekRequestMessage()->getString("User-Agent"), "");
2198
2199 LogPrint(0, LOG_SYSTEM, 0, "Failed to reply to %s request %llu from Executor %llu via WebSocket...", reply->isLongReq ? "LONG" : "SHORT", reply->gatewayRef, reply->processor);
2200 delete(wsData);
2201 delete(msg);
2202 requestMap.erase(i);
2203 delete(reply);
2204 mutex.leave();
2205 return false;
2206 }
2207 }
2208 else if ( (con=clients.find(reply->origin)) != clients.end()) {
2209 //LogPrint(0, LOG_SYSTEM, 0, "Gateway received reply for client %llu", reply->origin);
2210 if (reply->isLongReq)
2211 con->second.longAvgStats.add(reqSize);
2212 else
2213 con->second.shortAvgStats.add(reqSize);
2214 // avg = &(longAvgStatsClients[reply->origin]);
2215 // avg = &(shortAvgStatsClients[reply->origin]);
2216 //avg->add(reqSize);
2217 msg->setReference(reply->clientRef);
2218 msg->setFrom((uint32)reply->processor);
2219 msg->setStatus((uint16)reply->getStatus());
2220 if (channel->sendMessage(msg, reply->origin)) {
2221
2222 addToCallLog(++callLogCount, 1, 3, reply->isLongReq, reply->peekRequestMessage()->getTime("SOURCE"), 0, reply->processor, 0, reply->startTime, now, 0, GetTimeAge(reply->startTime), (uint8)reply->peekRequestMessage()->getInt("HTTP_OPERATION"), reply->peekRequestMessage()->getSize(), msg->getSize(), "Success",
2223 reply->peekRequestMessage()->getString("URI"), reply->peekRequestMessage()->getString("User-Agent"), "");
2224
2225 LogPrint(0, LOG_SYSTEM, 3, "Replied to %s request %llu from Executor %llu via DataMessage...", reply->isLongReq ? "LONG" : "SHORT", reply->gatewayRef, reply->processor);
2226 con->second.count++;
2228 // success
2229 delete(msg);
2230 requestMap.erase(i);
2231 delete(reply);
2232 mutex.leave();
2233 return true;
2234 }
2235 else {
2236
2237 addToCallLog(++callLogCount, 1, 3, reply->isLongReq, reply->peekRequestMessage()->getTime("SOURCE"), 0, reply->processor, 0, reply->startTime, now, 0, GetTimeAge(reply->startTime), (uint8)reply->peekRequestMessage()->getInt("HTTP_OPERATION"), reply->peekRequestMessage()->getSize(), msg->getSize(), "Failed to send reply",
2238 reply->peekRequestMessage()->getString("URI"), reply->peekRequestMessage()->getString("User-Agent"), "");
2239
2240 LogPrint(0, LOG_SYSTEM, 0, "Failed to reply to %s request %llu from Executor %llu via DataMessage...", reply->isLongReq ? "LONG" : "SHORT", reply->gatewayRef, reply->processor);
2241 delete(msg);
2242 requestMap.erase(i);
2243 delete(reply);
2244 mutex.leave();
2245 return false;
2246 }
2247 }
2248
2249 // else it failed
2250 // ########
2251
2252 addToCallLog(++callLogCount, 1, 3, reply->isLongReq, reply->peekRequestMessage()->getTime("SOURCE"), 0, reply->processor, 0, reply->startTime, now, 0, GetTimeAge(reply->startTime), (uint8)reply->peekRequestMessage()->getInt("HTTP_OPERATION"), reply->peekRequestMessage()->getSize(), msg->getSize(), "Failed to find connection to reply to",
2253 reply->peekRequestMessage()->getString("URI"), reply->peekRequestMessage()->getString("User-Agent"), "");
2254
2255 LogPrint(0, LOG_SYSTEM, 0, "Failed to find anyone to reply to for %s request %llu from Executor %llu via DataMessage...", reply->isLongReq ? "LONG" : "SHORT", reply->gatewayRef, reply->processor);
2256 //printf("Failed request GWID: %llu\n", reply->gatewayRef);
2257 delete(msg);
2258 requestMap.erase(i);
2259 delete(reply);
2260 mutex.leave();
2261 return false;
2262}
2263
2264
2266 DataMessage* replyMsg;
2267 RequestReply* reply;
2268 isRunning = true;
2269 uint64 t = GetTimeNow();
2270 uint64 lastHeartbeatTest = GetTimeNow();
2271 std::map<uint64, RequestConnection>::iterator i, e = executors.end(), ii;
2272
2273 uint32 count = 0;
2274 while (shouldContinue) {
2275
2276 /*
2277 Short requests are added to the relevant queue and put directly into the execQ for sending
2278 Long requests are added to the relevant queue
2279 - when added check if we can send it right away, if so, add to execQ
2280 - if not, leave it in the relevant queue
2281 - when reply comes back from executor to free up a space
2282 choose the next one in the queue and add to execQ
2283 - periodically check queues for missed slots
2284 */
2285
2286 while (execQ.waitForAndTakeFirst(reply, 50)) {
2287 if (!mutex.enter(1000, __FUNCTION__)) {
2288 LogPrint(0, LOG_SYSTEM, 0, "Error locking Gateway Exec mutex");
2289 continue;
2290 }
2291 count++;
2292 t = GetTimeNow();
2293 if (
2294 (((ii = webClients.find(reply->origin)) != webClients.end()) && !ii->second.lastFailTime) ||
2295 (((ii = webSockets.find(reply->origin)) != webSockets.end()) && !ii->second.lastFailTime) ||
2296 (((ii = clients.find(reply->origin)) != clients.end()) && !ii->second.lastFailTime) ) {
2297
2298 if (channel->sendMessage(reply->peekRequestMessage(), reply->processor)) {
2299 //printf("[[[%.3fms]]]\n", GetTimeAge(t)/1000.0);
2300 executors[reply->processor].count++;
2301 //printf("--- GW -> EX[%llu] ---\n", (*i).conID);
2302 //fflush(stdout);
2303 //LogPrint(0, LOG_SYSTEM, 0, "Gateway sent request to Executor");
2304 reply->setStatus(SENT);
2305 execSentCount++;
2306 //LogPrint(0, LOG_SYSTEM, 0, "Sent %s request %llu to Executor %llu via network...", reply->isLongReq ? "LONG" : "SHORT", reply->gatewayRef, reply->processor);
2307 }
2308 else {
2309 reply->setStatus(SERVERERROR);
2310 // #########
2311 // reply to client: server error
2312 LogPrint(0, LOG_SYSTEM, 0, "Error sending %s request %llu to Executor %llu via network, giving up", reply->isLongReq ? "LONG" : "SHORT", reply->gatewayRef, reply->processor);
2313 }
2314 }
2315 else {
2316 LogPrint(0, LOG_SYSTEM, 0, "Cancelling %s request %llu bound for Executor %llu from disconnected Client %llu...", reply->isLongReq ? "LONG" : "SHORT", reply->gatewayRef, reply->processor, reply->origin);
2317 if (reply->isLongReq)
2318 executors[reply->processor].longReqQueue.completeRequest(reply);
2319 else
2320 executors[reply->processor].shortReqQueue.completeRequest(reply);
2321 requestMap.erase(reply->gatewayRef);
2322 delete(reply);
2323 }
2324 mutex.leave();
2325 }
2326
2327 if (mutex.enter(1000, __FUNCTION__)) {
2328 i = executors.begin();
2329 while (i != e) {
2330 while (i->second.longReqQProcessingSize < maxRequestProcessingSize) {
2331 if (reply = i->second.longReqQueue.getNextRequest()) {
2332 // is now marked as processing by the getNextRequest function
2333 // check that the client is still connected
2334 if (
2335 (((ii = webClients.find(reply->origin)) != webClients.end()) && !ii->second.lastFailTime) ||
2336 (((ii = webSockets.find(reply->origin)) != webSockets.end()) && !ii->second.lastFailTime) ||
2337 (((ii = clients.find(reply->origin)) != clients.end()) && !ii->second.lastFailTime)) {
2338
2339 i->second.longReqQProcessingSize++;
2340 execQ.add(reply);
2341 //LogPrint(0, LOG_SYSTEM, 0, "Scheduling %s request %llu to be sent to Executor %llu shortly for Client %llu...", reply->isLongReq ? "LONG" : "SHORT", reply->gatewayRef, reply->processor, reply->origin);
2342 }
2343 else {
2344 LogPrint(0, LOG_SYSTEM, 0, "Cancelling %s request %llu bound for Executor %llu from disconnected Client %llu...", reply->isLongReq ? "LONG" : "SHORT", reply->gatewayRef, reply->processor, reply->origin);
2345 if (reply->isLongReq)
2346 executors[reply->processor].longReqQueue.completeRequest(reply);
2347 else
2348 executors[reply->processor].shortReqQueue.completeRequest(reply);
2349 requestMap.erase(reply->gatewayRef);
2350 delete(reply);
2351 }
2352 }
2353 else
2354 break;
2355 }
2356
2358 if (i->second.removeStaleRequests(maxExecutorRequestTimeoutMS)) {
2359 while ( (reply = i->second.shortReqQueue.getNextTimedoutRequest()) ||
2360 (reply = i->second.longReqQueue.getNextTimedoutRequest()) ) {
2361 LogPrint(0, LOG_SYSTEM, 0, "Cancelling %s request %llu bound for Executor %llu due to timeout (%s)...",
2362 reply->isLongReq ? "LONG" : "SHORT", reply->gatewayRef, reply->processor,
2363 PrintTimeDifString(maxExecutorRequestTimeoutMS * 1000, false, false).c_str());
2364 replyMsg = new DataMessage();
2365 replyMsg->setReference(reply->gatewayRef);
2366 if (replyXML)
2367 replyMsg->setString("XML", "<error>Timeout processing request</error>");
2368 else
2369 replyMsg->setString("JSON", "{\"error\": \"Timeout processing request\"}\n");
2370 replyQ.add(replyMsg);
2371 requestMap.erase(reply->gatewayRef);
2372 delete(reply);
2373 }
2374 }
2375 }
2376 i++;
2377 }
2378
2379 // Check executors for heartbeat timeout
2380 if (executorHeartbeatTimeout && (GetTimeAgeMS(lastHeartbeatTest) > 5000)) {
2381 i = executors.begin();
2382 while (i != e) {
2383 if (i->second.conID && (GetTimeAgeMS(i->second.lastStatusTime) > (int32)executorHeartbeatTimeout)) {
2384 LogPrint(0, LOG_SYSTEM, 0, "Gateway found frozen executor ID %u (%s), current queue size short: %u long: %u, sending restart message",
2385 i->second.conID, PrintTimeDifString(GetTimeAge(i->second.lastStatusTime), false, true).c_str(),
2386 i->second.longReqQueue.getCount(), i->second.shortReqQueue.getCount());
2387
2388 // Redistribute queued requests to other executors
2389 std::list<RequestReply*> shortQueue = i->second.shortReqQueue.takeQueue();
2390 std::list<RequestReply*> longQueue = i->second.longReqQueue.takeQueue();
2391
2392 if (!sendRestartToExecutor(i->second.conID))
2393 LogPrint(0, LOG_SYSTEM, 0, "Gateway was unable to send restart message to frozen executor ID %u", i->second.conID);
2394 i = executors.erase(i);
2395
2396 distributeDeadExecutorRequests(shortQueue, longQueue);
2397
2398 }
2399 else
2400 i++;
2401 }
2402 lastHeartbeatTest = GetTimeNow();
2403 }
2404 }
2405 mutex.leave();
2406 }
2407 isRunning = false;
2408 return true;
2409}
2410
2411
2412bool RequestGateway::distributeDeadExecutorRequests(std::list<RequestReply*>& shortQueue, std::list<RequestReply*>& longQueue) {
2413
2414 RequestReply* reply;
2415 std::list<RequestReply*>::iterator rI, rE;
2416
2417 std::map<uint64, RequestConnection>::iterator i, e = executors.end();
2418
2419 while (shortQueue.size() || longQueue.size()) {
2420 if (shortQueue.size()) {
2421 reply = shortQueue.front();
2422 shortQueue.pop_front();
2423 if (reply) {
2424 reply->setStatus(QUEUED);
2426 }
2427 }
2428 if (longQueue.size()) {
2429 reply = longQueue.front();
2430 longQueue.pop_front();
2431 if (reply) {
2432 reply->setStatus(QUEUED);
2434 }
2435 }
2436 }
2437 return true;
2438}
2439
2441 DataMessage* msg;
2442 isRunning = true;
2443
2444 uint64 lastMaintenance = GetTimeNow();
2445 std::map<uint64, RequestConnection>::iterator i, e = clients.end();
2446
2447 while (shouldContinue) {
2448 while (msg = replyQ.waitForAndTakeFirst(50))
2449 replyToClient(msg);
2450
2451 if (GetTimeAgeMS(lastMaintenance) > 5000) {
2452 i = clients.begin();
2453 while (i != e) {
2454 if (i->second.lastFailTime && (GetTimeAgeMS(i->second.lastFailTime) > 5000))
2455 i = clients.erase(i);
2456 else
2457 i++;
2458 }
2459 }
2460 }
2461 isRunning = false;
2462 return true;
2463}
2464
2466 if (arg == NULL) thread_ret_val(1);
2467 thread_ret_val((int)(((RequestGateway*)arg)->runExec() ? 0 : 1));
2468}
2469
2471 if (arg == NULL) thread_ret_val(1);
2472 thread_ret_val((int)(((RequestGateway*)arg)->runClient() ? 0 : 1));
2473}
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492bool RequestGateway::UnitTest(uint16 port, uint32 executorCount, uint32 gatewayCount, uint32 clientCount, uint32 webCount, uint32 webSocketCount, double sendfreq, int32 reconnect, uint32 payload, uint32 processTime, uint32 runtime) {
2493 uint16 n, m;
2494
2495 //uint8 encryption = SSLENC;
2496 uint8 encryption = NOENC;
2497
2498 //DataMessage *msg;
2499 //RequestReply* reply;
2500 std::list<RequestReply*> replyList;
2501 //RequestStatus status;
2502
2503 RequestGateway* gateway;
2504 TestRequestExecutor* exec;
2505 //RequestClient* client;
2506 TestRequestClient* testClient;
2507 TestWebRequestClient* testWebClient;
2508 TestWebSocketRequestClient* testWebSocketClient;
2509
2510 std::vector<RequestGateway*> gateways;
2511 std::vector<TestRequestExecutor*> executors;
2512 std::vector<RequestClient*> clients;
2513 std::vector<TestRequestClient*> testClients;
2514 std::vector<TestWebRequestClient*> testWebClients;
2515 std::vector<TestWebSocketRequestClient*> testWebSocketClients;
2516
2517 std::string sslCertPath = "../cert";
2518 std::string sslKeyPath = "../pkey";
2519
2520 // Start N gateways
2521 unittest::progress(5, "start gateways");
2522 for (n=0; n<gatewayCount; n++) {
2523 gateway = new RequestGateway(n);
2524 if (encryption == SSLENC) {
2525 if (gateway->init(sslCertPath.c_str(), sslKeyPath.c_str()))
2526 gateways.push_back(gateway);
2527 else {
2528 unittest::fail("RequestGateway test: couldn't initialise gateway %u", n);
2529 return false;
2530 }
2531 }
2532 else {
2533 if (gateway->init())
2534 gateways.push_back(gateway);
2535 else {
2536 unittest::fail("RequestGateway test: couldn't initialise gateway %u", n);
2537 return false;
2538 }
2539 }
2540 if (!gateway->addPort(port+n, encryption, true)) {
2541 unittest::fail("RequestGateway test: couldn't bind gateway %u public port %u", n, port + n);
2542 return false;
2543 }
2544 if (!gateway->addPort(port-100+n, encryption)) {
2545 unittest::fail("RequestGateway test: couldn't bind gateway %u port %u", n, port-100+n);
2546 return false;
2547 }
2548 gateway->setResponseType("JSON");
2549 gateway->setWebServerInfo("Psyclone", "../html/", "index.html");
2550 }
2551
2552 // Start N executors
2553 unittest::progress(20, "start executors");
2554 for (n=0; n<executorCount; n++) {
2555 exec = new TestRequestExecutor(n+1, processTime);
2556 exec->addLongRequestName("longtest");
2557 for (m=0; m<gatewayCount; m++) {
2558 if (!exec->addGateway(m, "localhost", port-100+m, encryption)) {
2559 unittest::fail("RequestGateway test: couldn't add gateway %u to executor %u", m, n);
2560 return false;
2561 }
2562 }
2563 //if (!exec->addGateway(m, "localhost", port - 100 + m, encryption)) {
2564 // LogPrint(0, LOG_SYSTEM, 0, "Couldn't add gateway %u to executor %u...", m, n);
2565 // return false;
2566 //}
2567 if (!gatewayCount) {
2568 if (!exec->addGateway(m, "localhost", port-100, encryption)) {
2569 unittest::fail("RequestGateway test: couldn't add gateway 0 to executor %u", n);
2570 return false;
2571 }
2572 //if (!exec->addGateway(m+1, "localhost", port - 99, encryption)) {
2573 // LogPrint(0, LOG_SYSTEM, 0, "Couldn't add gateway 1 to executor %u...", n);
2574 // return false;
2575 //}
2576 }
2577 executors.push_back(exec);
2578 }
2579
2580 unittest::detail("Gateway(s) and Executor(s) set up and started\n");
2581 utils::Sleep(1000);
2582
2583 // Start N clients
2584 unittest::progress(35, "start clients");
2585 for (n = 0; n < clientCount; n++) {
2586 testClient = new TestRequestClient(n+1);
2587 if (!testClient->init(port, gatewayCount, sendfreq, reconnect, payload, "longtest")) {
2588 unittest::fail("RequestGateway test: couldn't initialise test client %u", n);
2589 return false;
2590 }
2591 testClients.push_back(testClient);
2592 }
2593
2594 // Start N web clients
2595 for (n = 0; n < webCount; n++) {
2596 testWebClient = new TestWebRequestClient(n + 1);
2597 if (!testWebClient->init(port, gatewayCount, sendfreq, reconnect, payload, "longtest")) {
2598 unittest::fail("RequestGateway test: couldn't initialise test web client %u", n);
2599 return false;
2600 }
2601 testWebClients.push_back(testWebClient);
2602 }
2603
2604 // Start N websocket clients
2605 for (n = 0; n < webSocketCount; n++) {
2606 testWebSocketClient = new TestWebSocketRequestClient(n + 1);
2607 if (!testWebSocketClient->init(port, gatewayCount, sendfreq, reconnect, payload, "longtest")) {
2608 unittest::fail("RequestGateway test: couldn't initialise test websocket client %u", n);
2609 return false;
2610 }
2611 testWebSocketClients.push_back(testWebSocketClient);
2612 }
2613
2614 // Drive a bounded, OBSERVABLE batch of synchronous requests through a real
2615 // RequestClient so we can produce a concrete pass/fail and latency metric.
2616 // (The Test* clients above exercise the harness but do not expose counters.)
2617 unittest::progress(45, "synchronous request batch");
2618 uint32 reqSent = 0, reqOK = 0;
2619 double totalLatencyUs = 0.0;
2620 {
2621 RequestClient probe;
2622 bool added = false;
2623 for (m = 0; m < (gatewayCount ? gatewayCount : 1); m++) {
2624 if (probe.addGateway(m, "localhost", port + m, encryption))
2625 added = true;
2626 }
2627 if (!added) {
2628 unittest::fail("RequestGateway test: probe client couldn't add any gateway");
2629 return false;
2630 }
2631 probe.waitForConnection(3000);
2632
2633 // Warm up the full client->gateway->executor pipeline before measuring.
2634 // The executor<->gateway registration can lag the client connection, so a
2635 // cold request can stall for its entire timeout. Poll with short-timeout
2636 // single requests until one round-trips (bounded), so the measured batch
2637 // below always runs against a hot pipeline and completes quickly. Without
2638 // this, cold requests occasionally burned 3s each and pushed the whole
2639 // test past its harness timeout.
2640 uint64 warmupStart = GetTimeNow();
2641 bool warm = false;
2642 while (!warm && GetTimeAgeMS(warmupStart) < 15000) {
2643 DataMessage* wmsg = new DataMessage();
2644 wmsg->setString("URI", "shorttest");
2645 RequestReply* wreply = probe.postRequest(wmsg);
2646 if (!wreply) { delete wmsg; utils::Sleep(100); continue; }
2647 if (wreply->waitForResult(1000) == SUCCESS)
2648 warm = true;
2649 probe.finishRequest(wreply);
2650 }
2651 if (!warm) {
2652 unittest::fail("RequestGateway test: pipeline never became ready (no request round-tripped within 15s)");
2653 return false;
2654 }
2655
2656 // Drive a bounded batch of synchronous requests end-to-end through the
2657 // full client->gateway->executor->reply->client pipeline and confirm
2658 // every one comes back successfully.
2659 const uint32 probeCount = 20;
2660 for (uint32 r = 0; r < probeCount; r++) {
2661 DataMessage* msg = new DataMessage();
2662 msg->setString("URI", "shorttest");
2663 RequestReply* reply = probe.postRequest(msg);
2664 if (!reply) { delete msg; continue; }
2665 reqSent++;
2666 if (reply->waitForResult(3000) == SUCCESS) {
2667 reqOK++;
2668 totalLatencyUs += (double)reply->getRequestDuration();
2669 }
2670 probe.finishRequest(reply);
2671 }
2672 }
2673 unittest::detail("Synchronous probe: %u/%u requests succeeded\n", reqOK, reqSent);
2674
2675 uint64 runtimeStart = GetTimeNow();
2676
2677 // runtime==0 in the original means "run forever"; the harness must terminate,
2678 // so callers always pass a small positive runtime (see Test_RequestGateway).
2679 if (!runtime) {
2680 unittest::fail("RequestGateway test: runtime==0 would run forever; refusing");
2681 return false;
2682 }
2683
2684 unittest::detail("Running background harness for %s...\n", PrintTimeDifString((uint64)runtime*1000000).c_str());
2685 unittest::progress(70, "run harness");
2686 while (GetTimeAgeMS(runtimeStart) < ((int32)runtime * 1000))
2687 utils::Sleep(100);
2688
2689 // Shut down the clients...
2690 unittest::progress(85, "teardown");
2691 std::vector<TestRequestClient*>::iterator ci, ce;
2692 for (ci = testClients.begin(), ce = testClients.end(); ci != ce; ci++) {
2693 (*ci)->finishUp();
2694 while ((*ci)->isStillRunning())
2695 utils::Sleep(100);
2696 delete(*ci);
2697 }
2698
2699 std::vector<TestWebRequestClient*>::iterator wi, we;
2700 for (wi = testWebClients.begin(), we = testWebClients.end(); wi != we; wi++) {
2701 (*wi)->finishUp();
2702 while ((*wi)->isStillRunning())
2703 utils::Sleep(100);
2704 delete(*wi);
2705 }
2706
2707 std::vector<TestWebSocketRequestClient*>::iterator si, se;
2708 for (si = testWebSocketClients.begin(), se = testWebSocketClients.end(); si != se; si++) {
2709 (*si)->finishUp();
2710 while ((*si)->isStillRunning())
2711 utils::Sleep(100);
2712 delete(*si);
2713 }
2714
2715 utils::Sleep(200);
2716 std::vector<TestRequestExecutor*>::iterator ei, ee;
2717 for (ei = executors.begin(), ee = executors.end(); ei != ee; ei++)
2718 delete(*ei);
2719
2720 utils::Sleep(200);
2721 std::vector<RequestGateway*>::iterator gi, ge;
2722 for (gi = gateways.begin(), ge = gateways.end(); gi != ge; gi++)
2723 delete(*gi);
2724
2725 // A real pass requires that the bounded synchronous batch actually completed.
2726 if (reqSent == 0) {
2727 unittest::fail("RequestGateway test: probe client posted no requests");
2728 return false;
2729 }
2730 if (reqOK < reqSent) {
2731 unittest::fail("RequestGateway test: only %u of %u requests completed successfully", reqOK, reqSent);
2732 return false;
2733 }
2734
2735 double avgLatencyUs = totalLatencyUs / (double)reqOK;
2736 unittest::metric("requests_completed", (double)reqOK, "", true);
2737 unittest::metric("request_success_rate", 100.0 * (double)reqOK / (double)reqSent, "%", true);
2738 unittest::metric("avg_request_latency", avgLatencyUs, "us", false);
2739
2740 unittest::progress(100, "done");
2741 return true;
2742
2743/* utils::Sleep(1000000000);
2744
2745 for (n=0; n<clientCount; n++) {
2746 client = new RequestClient();
2747 for (m=0; m<gatewayCount; m++) {
2748 if (!client->addGateway(m, "localhost", port+m, encryption)) {
2749 LogPrint(0, LOG_SYSTEM, 0, "Couldn't add gateway %u to client %u...\n", m, n);
2750 return false;
2751 }
2752 }
2753 clients.push_back(client);
2754 }
2755
2756 char* bigdata = new char[payload];
2757 memset(bigdata, 1, payload);
2758
2759 uint32 requestCount = 300;
2760 // Send asynchronous binary requests
2761 for (n=0; n<clientCount; n++) {
2762 client = clients[n];
2763 for (m=0; m<requestCount/2; m++) {
2764 msg = new DataMessage();
2765 msg->setString("URI", "shorttest");
2766 if (reply = client->postRequest(msg)) {
2767 reply->customRef = 1;
2768 replyList.push_back(reply);
2769 }
2770 else {
2771 LogPrint(0, LOG_SYSTEM, 0, "Couldn't post short query %u via client %u...\n", m, n);
2772 delete(msg);
2773 return false;
2774 }
2775 //printf("Posted short query %u via client %u...\n", m, n);
2776 msg = new DataMessage();
2777 msg->setString("URI", "longtest");
2778 msg->setData("BigData", bigdata, payload);
2779 if (reply = client->postRequest(msg)) {
2780 reply->customRef = 2;
2781 replyList.push_back(reply);
2782 }
2783 else {
2784 LogPrint(0, LOG_SYSTEM, 0, "Couldn't post long query %u via client %u...\n", m, n);
2785 delete(msg);
2786 utils::Sleep(50000);
2787 return false;
2788 }
2789 //printf("Posted long query %u via client %u...\n", m, n);
2790 }
2791 utils::Sleep(200);
2792 }
2793 //printf("Finished posting, now waiting...\n");
2794
2795 // Wait for results of asynchronous requests
2796
2797 std::list<RequestReply*>::iterator i,e;
2798
2799 uint64 startTime = GetTimeNow();
2800 uint32 stillWaiting, failed, timeout, errors, success, total;
2801 stillWaiting = failed = timeout = errors = success = total = 0;
2802 while (true) {
2803 stillWaiting = failed = timeout = errors = success = total = 0;
2804 for (i=replyList.begin(),e=replyList.end(); i!=e; i++) {
2805 reply = *i;
2806 status = reply->getStatus();
2807 total++;
2808 switch(status) {
2809 case SUCCESS:
2810 //if (reply->customRef == 2) {
2811 // longStats.add((double)reply->getRequestDuration());
2812 // //printf("--- [%u] --- Long request %.3fms \n", reply->peekReplyMessage()->getFrom(), reply->getRequestDuration()/1000.0);
2813 //}
2814 //else if (reply->customRef == 1) {
2815 // shortStats.add((double)reply->getRequestDuration());
2816 // //printf("--- [%u] --- Short request %.3fms \n", reply->peekReplyMessage()->getFrom(), reply->getRequestDuration()/1000.0);
2817 //}
2818 success++;
2819 break;
2820 case FAILED:
2821 failed++;
2822 break;
2823 case LOCALERROR:
2824 case NETWORKERROR:
2825 case SERVERERROR:
2826 case TOOBUSY:
2827 errors++;
2828 break;
2829 case TIMEOUT:
2830 timeout++;
2831 break;
2832 default:
2833 stillWaiting++;
2834 break;
2835 }
2836 }
2837 if (!stillWaiting || GetTimeAgeMS(startTime) > 60000)
2838 break;
2839 //if (stillWaiting != total) {
2840 LogPrint(0, LOG_SYSTEM, 0, "----- [%u/%u] success: %u failed: %u timeout: %u errors: %u\n",
2841 stillWaiting, total, success, failed, timeout, errors);
2842 //printf("--- [%u] --- Long request %.3fms \n", reply->peekReplyMessage()->getFrom(), reply->getRequestDuration()/1000.0);
2843 //}
2844 utils::Sleep(200);
2845 }
2846 if (total == success) {
2847 Stats shortStats, longStats;
2848 for (i=replyList.begin(),e=replyList.end(); i!=e; i++) {
2849 reply = *i;
2850 if (reply->customRef == 2) {
2851 longStats.add((double)reply->getRequestDuration());
2852 LogPrint(0, LOG_SYSTEM, 0, "--- [%u] --- Long request %.3fms \n", reply->peekReplyMessage()->getFrom(), reply->getRequestDuration()/1000.0);
2853 }
2854 else if (reply->customRef == 1) {
2855 shortStats.add((double)reply->getRequestDuration());
2856 LogPrint(0, LOG_SYSTEM, 0, "--- [%u] --- Short request %.3fms \n", reply->peekReplyMessage()->getFrom(), reply->getRequestDuration()/1000.0);
2857 }
2858 }
2859 LogPrint(0, LOG_SYSTEM, 0, "[%u] success - short stats: %.3f (%.3f) long stats: %.3f (%.3f)\n",
2860 success, shortStats.getAverage()/1000.0, shortStats.getStdDev()/1000.0, longStats.getAverage()/1000.0, longStats.getStdDev()/1000.0);
2861 }
2862 else {
2863 LogPrint(0, LOG_SYSTEM, 0, "[%u/%u] success: %u failed: %u timeout: %u errors: %u\n",
2864 stillWaiting, total, success, failed, timeout, errors);
2865 }
2866
2867 utils::Sleep(500000);
2868 // Send synchronous HTTP requests
2869 // #########################
2870
2871 std::vector<RequestClient*>::iterator ci, ce;
2872 for (ci = clients.begin(), ce = clients.end(); ci != ce; ci++)
2873 delete(*ci);
2874 std::vector<TestRequestExecutor*>::iterator ei, ee;
2875 for (ei = executors.begin(), ee = executors.end(); ei != ee; ei++)
2876 delete(*ei);
2877 std::vector<RequestGateway*>::iterator gi, ge;
2878 for (gi = gateways.begin(), ge = gateways.end(); gi != ge; gi++)
2879 delete(*gi);
2880
2881 return true;*/
2882}
2883
2884
2885// Adapter for the multi-argument RequestGateway::UnitTest. The framework can only
2886// register zero-arg bool() tests, so this spins up a small, fully self-contained
2887// localhost harness with a SHORT bounded runtime and returns a real pass/fail.
2888static bool Test_RequestGateway() {
2889 // port 38200 public, 38100 internal (port-100); 1 gateway, 1 executor,
2890 // 1 client, no web/websocket; modest send freq; no reconnect; 4 KB payload;
2891 // small processing time; 3-second bounded runtime so it terminates quickly.
2893 /*port*/ 38200,
2894 /*executorCount*/ 1,
2895 /*gatewayCount*/ 1,
2896 /*clientCount*/ 1,
2897 /*webCount*/ 0,
2898 /*webSocketCount*/0,
2899 /*sendfreq*/ 20.0,
2900 /*reconnect*/ 0,
2901 /*payload*/ 4096,
2902 /*processTime*/ 5,
2903 /*runtime*/ 3);
2904}
2905
2908 "Request queue add/get/priority/drain", "network");
2910 "Gateway/executor/client request round-trip over localhost", "network");
2911}
2912
2913
2914}
std::string base64_encode(std::string string_to_encode)
Base64-encode a string's bytes.
Definition Base64.cpp:44
#define NOENC
Plain, unencrypted transport.
#define SSLENC
SSL/TLS encryption (requires build with _USE_SSL_).
#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_DELETE
DELETE.
#define HTTP_GET
GET.
#define HTTP_PUT
PUT.
#define HTTP_SERVER_NOREPLY
500 (no reply from backend server)
#define PROTOCOL_HTTP_SERVER
Serve HTTP: parse requests, send replies (server role).
#define HTTP_POST
POST.
#define PROTOCOL_MESSAGE
CMSDK binary DataMessage protocol (size-prefixed frames).
Request-system router: RequestGateway accepts client requests (binary/HTTP/WebSocket),...
#define MAXVALUINT32
Definition Types.h:87
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
One entry of the gateway's call log: who called what, how it was routed, how long each stage took,...
uint64 executorID
Executor that processed the request.
uint64 endTime
Reply completion time (ms epoch).
uint32 requestSize
Request payload size in bytes.
uint16 port
Gateway port the call arrived on.
uint64 queueTime
Time spent queued before dispatch (µs).
uint64 processTime
Time spent processing in the executor (µs).
uint8 conType
Ingress type — 1: client 2: web 3: websocket.
uint8 messageType
Wire format — 1: message 2: http 3: https.
uint64 source
Caller endpoint packed as uint64.
uint32 replySize
Reply payload size in bytes.
std::string output
Output/diagnostic note.
std::string agent
Caller's User-Agent (web ingress).
uint64 clientID
Client connection id.
uint64 startTime
Request arrival time (ms epoch).
uint8 requestType
HTTP_* method id (for web ingress).
std::string requestPath
Request name/URI.
bool longRequest
Whether the request was classified long.
std::string status
Outcome status text.
The central Psyclone data container: a self-contained binary message with typed, named user entries.
bool setString(const char *key, const char *value)
setString(const char* key, const char* value)
bool getInt(const char *key, int64 &value)
getInt(const char* key, int64& value)
bool setInt(const char *key, int64 value)
setInt(const char* key, int64 value)
bool setStatus(uint16 status)
setStatus(uint16 status)
const char * getData(const char *key, uint32 &size)
getData(const char* key, uint32& size)
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 setRecvTime(uint64 time)
setRecvTime(uint64 time)
bool setFrom(uint32 from)
setFrom(uint32 from)
bool setReference(uint64 ref)
setReference(uint64 ref) Set message reference
uint64 getTime(const char *key)
getTime(const char* key)
const char * getString(const char *key)
getString(const char* key)
A parsed or generated HTTP response.
bool createFromFile(uint64 time, const char *serverName, uint64 ifLastMod, bool keepAlive, bool cache, const char *filename)
Build a response by reading a file from disk (MIME type from extension).
static HTTPReply * CreateAuthorizationReply(const char *realm)
Build a 401 reply requesting Basic authentication.
bool createOptionsResponse(uint8 status, uint64 time, const char *serverName, bool keepAlive, const char *origin, const char *operations)
Build a CORS-preflight (OPTIONS) response.
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.
bool createErrorPage(uint8 status, uint64 time, const char *serverName, bool keepAlive)
Build a canned error page for the given status.
A parsed or generated HTTP request (also used for WebSocket upgrade handshakes).
const char * getBasicAuthorization()
const char * getHeaderEntry(const char *entry)
Look up a header field (case-insensitive).
DataMessage * convertToMessage()
Convert this HTTP request into a binary DataMessage (URI, params and content mapped to message fields...
uint32 contentLength
Body length (from Content-Length / parsing), in bytes.
uint8 type
HTTP_* method id.
uint64 ifModifiedSince
Parsed If-Modified-Since timestamp (ms epoch; 0 = absent).
uint64 source
Packed uint64 endpoint of the sender (0 if local).
uint64 endReceiveTime
Timestamp when the request was fully received (ms epoch).
const char * getParameter(const char *entry)
Look up a URI query or form parameter by name.
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.
uint64 jmSize
Total size of jmData in bytes.
DataMessage * convertToMessage()
Convert this container into a binary DataMessage.
std::map< uint32, JSONMEntry > entries
Attachment metadata by chunk index.
std::string getJSON()
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.
Central owner of all channels, listeners and connections in a process.
Application-facing client of the request system: posts DataMessage requests to one or more RequestGat...
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 addGateway(uint32 id, std::string addr, uint16 port, uint8 encryption=NOENC)
Register a gateway to connect to (may be called several times for redundancy).
bool waitForConnection(uint32 timeoutMS)
Block until a gateway connection is established.
bool addGateway(uint32 id, std::string addr, uint16 port, uint8 encryption=NOENC)
Register a gateway to connect to (repeatable for redundancy).
bool addLongRequestName(const char *name)
Register a request name to be treated as a long-running request.
uint32 longReqLimit
Concurrent long-request cap per executor.
bool setResponseType(const char *type)
Choose the reply serialisation ("xml" or "json").
std::map< std::string, uint64 > httpAuth
Basic-auth users (user -> credential hash).
std::map< uint64, RequestReply * > requestMap
In-flight requests by gateway ref id.
bool addAuthUser(const char *user, const char *password)
Add a basic-auth user for the web interface/APIs.
bool distributeDeadExecutorRequests(std::list< RequestReply * > &shortQueue, std::list< RequestReply * > &longQueue)
Requeue a dead executor's outstanding requests onto surviving executors.
friend THREAD_RET THREAD_FUNCTION_CALL RequestGatewayClientRun(THREAD_ARG arg)
Thread entry point for the gateway's client-side loop.
uint64 systemStartTime
Gateway start time (ms epoch), for uptime reporting.
uint64 execSentCount
Messages sent to executors.
bool receiveWebsocketData(WebsocketData *wsData, NetworkChannel *channel, uint64 conid)
NetworkReceiver hook: WebSocket ingress carrying request payloads.
uint32 executorHeartbeatTimeout
Silence threshold before an executor is declared dead (ms).
std::map< uint64, RequestConnection > webSockets
Connected WebSocket clients by id.
bool receiveMessage(DataMessage *msg, NetworkChannel *channel, uint64 conid)
NetworkReceiver hook: binary requests/replies/heartbeats from clients and executors.
NetworkChannel * channel
Channel carrying all gateway traffic.
uint64 callLogCount
Total calls logged since start.
utils::Mutex callLogMutex
Guards the call log.
bool callInternalAPI(const char *apiName, HTTPRequest *req, NetworkChannel *channel, uint64 conid)
Handle a management/API call on the internal API label.
std::map< uint64, RequestConnection > executors
Connected executors by id.
uint64 lastRefID
Last issued gateway reference id.
std::string indexFilename
Default index file name.
std::string externalAPITitle
URI label of the external (public) API.
bool setCacheFiles(bool cache)
Enable/disable HTTP caching headers for served files.
bool setMaxExecutorRequestTimeout(uint32 timeout)
Set the maximum time a request may sit with an executor before being failed/retried.
bool setExecutorHeartbeatTimeout(uint32 timeout)
Set how long an executor may go silent before being declared dead (its queue is then redistributed).
std::string webServerName
Server header name of the built-in web server.
uint16 port
Primary listening port.
utils::WaitQueue< RequestReply * > execQ
Requests awaiting executor dispatch.
uint32 maxRequestProcessingSize
Backpressure: max requests dispatched concurrently.
std::vector< std::string > longReqNames
Request names classified as long.
bool receiveHTTPRequest(HTTPRequest *req, NetworkChannel *channel, uint64 conid)
NetworkReceiver hook: web ingress — serves files, APIs, or converts the request into an internal Data...
std::string rootdir
Document root for file serving.
bool callExternalAPI(const char *apiName, HTTPRequest *req, NetworkChannel *channel, uint64 conid)
Handle a public API call on the external API label.
std::map< uint64, RequestConnection > webClients
Connected HTTP clients by id.
bool addToCallLog(uint64 id, uint8 conType, uint8 messageType, bool longRequest, uint64 source, uint16 port, uint64 executorID, uint64 clientID, uint64 startTime, uint64 endTime, uint64 queueTime, uint64 processTime, uint8 requestType, uint32 requestSize, uint32 replySize, const char *status, const char *requestPath, const char *agent, const char *output)
Append one completed call to the rolling call log (trimmed to callLogMax).
uint64 execReceivedCount
Messages received from executors.
bool setQueuingParameters(uint32 maxRequestQueueSize, uint32 maxRequestProcessingSize, uint32 priorityThreshold)
Tune queuing/backpressure behaviour.
bool sslSupport
True when an SSL certificate was configured.
uint64 clientReceivedCount
Messages received from clients.
std::string versionString
Version shown in status pages.
MovingAverage shortAvgStats
Rolling latency stats for short requests.
bool addGateway(uint32 id, std::string addr, uint16 port)
Register a peer gateway (multi-gateway federation).
uint32 threadIDClient
Client-side worker thread id.
MovingAverage longAvgStats
Rolling latency stats for long requests.
bool cacheFiles
Send caching headers for served files.
uint32 maxRequestQueueSize
Backpressure: max queued requests before TOOBUSY.
bool setLongRequestLimit(uint32 limit)
Cap concurrent long requests per executor.
std::string internalAPITitle
URI label of the internal (management) API.
std::map< uint64, RequestConnection > clients
Connected binary clients by id.
bool setExternalAPILabel(const char *label)
Set the URI label under which the external (public) API is exposed.
uint64 getBestExecutorID(std::string requestString, uint32 reqSize, bool &isLongReq)
Pick the executor to receive a request — the core load-balancing decision, based on reported queue si...
NetworkManager * manager
Owned network stack (listeners + connections).
bool addPort(uint16 port, uint8 encryption, bool enableHTTP=false, uint32 timeout=3000)
Open a listening port for clients/executors (call before init()).
uint32 maxExecutorRequestTimeoutMS
Max time a request may sit with an executor (ms).
bool sendRestartToExecutor(uint64 id)
Ask an executor to restart itself (sent when it misbehaves).
bool setInternalAPILabel(const char *label)
Set the URI label under which the internal (management) API is exposed.
utils::WaitQueuePointer< DataMessage * > replyQ
Replies awaiting client routing.
std::list< CallLogEntry * > callLog
Rolling log of completed calls (owned).
bool setWebServerInfo(const char *name, const char *rootdir, const char *indexfile)
Configure the built-in file web server.
RequestGateway(uint32 id, const char *version=NULL)
bool runClient()
Client-side worker loop: route replies back to callers, expire timed-out requests.
friend THREAD_RET THREAD_FUNCTION_CALL RequestGatewayExecRun(THREAD_ARG arg)
Thread entry point for the gateway's executor-side loop.
bool runExec()
Executor-side worker loop: dispatch queued requests, watch heartbeats, redistribute dead executors' w...
utils::Mutex mutex
Guards shared gateway state.
uint32 priorityThreshold
Queue length above which priority ordering applies.
static bool UnitTest(uint16 port, uint32 executorCount, uint32 gatewayCount, uint32 clientCount, uint32 webCount, uint32 webSocketCount, double sendfreq, int32 reconnect, uint32 payload, uint32 processTime, uint32 runtime)
End-to-end stress test spinning up a gateway plus the given numbers of executors, extra gateways and ...
bool addToRequestQueue(DataMessage *msg, uint64 origin, uint64 conID, uint64 clientRef)
Wrap an incoming message in a RequestReply and enqueue it for dispatch.
bool addRequestReplyToRequestQueue(RequestReply *reply)
Enqueue an already-wrapped request (e.g.
uint32 callLogMax
Max entries kept in callLog.
bool init(const char *sslCertPath=NULL, const char *sslKeyPath=NULL)
Start the gateway threads and network stack.
bool receiveNetworkEvent(NetworkEvent *evt, NetworkChannel *channel, uint64 conid)
NetworkReceiver hook: connect/disconnect events for clients and executors.
bool replyXML
Reply in XML (true) or JSON (false).
bool replyToClient(DataMessage *msg)
Route a reply message back to the client that issued the request.
uint64 clientSentCount
Messages sent to clients.
std::list< RequestReply * > takeQueue()
Atomically take all queued requests (used to redistribute a dead executor's backlog).
bool init(uint32 id)
Initialise the queue.
RequestReply * getNextRequest()
Pop the next request to dispatch.
std::list< RequestReply * > requestQ
The ordered pending requests.
bool completeRequest(RequestReply *req)
Mark a request as done and remove it from tracking.
bool addRequest(RequestReply *req, double priority=0)
Insert a request, ordered by priority (higher = closer to the front).
uint32 removeStaleRequests(uint32 ttlMS)
Fail and remove requests older than ttlMS milliseconds.
static bool UnitTest()
Self-test of queue ordering/priority behaviour.
utils::Mutex mutex
Guards requestQ.
RequestReply * getNextTimedoutRequest()
Find and remove the next request whose deadline has passed.
Future/handle for one in-flight request: holds the request message, the eventual reply,...
uint64 gatewayRef
Gateway-side reference id.
DataMessage * peekRequestMessage()
bool isLongReq
True when classified as a long-running request (separate queue/limits).
bool setStatus(RequestStatus status)
Set the current lifecycle status (wakes waiters when terminal).
RequestStatus getStatus()
uint64 startTime
When the request was created (ms epoch).
uint64 origin
Originating endpoint/connection (packed uint64), for reply routing.
RequestStatus waitForResult(uint32 timeoutMS)
Block until the request reaches a terminal status or the timeout expires.
uint64 processor
Executor id that processed (or is processing) the request.
uint64 clientRef
Client-side reference id correlating request and reply.
bool giveRequestMessage(DataMessage *msg)
Attach the outgoing request message, transferring ownership.
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.
uint32 threadID
ThreadManager slot ID of the worker thread (0 until known).
bool shouldContinue
Loop-continuation flag; cleared by stop().
Load-test client driving a RequestGateway with binary-protocol requests at a configurable frequency/p...
bool init(uint16 port, uint32 gatewayCount, double sendfreq, int32 reconnectMS, uint32 payload, const char *longTestName)
Test executor that services requests with a configurable artificial processing delay; used by Request...
Load-test client driving a RequestGateway over plain HTTP web requests.
bool init(uint16 port, uint32 gatewayCount, double sendfreq, int32 reconnectMS, uint32 payload, const char *longTestName)
Load-test client driving a RequestGateway over WebSocket connections.
bool init(uint16 port, uint32 gatewayCount, double sendfreq, int32 reconnectMS, uint32 payload, const char *longTestName)
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 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.
uint64 contentSize
Payload bytes accumulated so far.
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 source
Packed uint64 endpoint of the sender (0 if local).
uint64 GetTimeNow()
Return the current absolute time (µs since year 0) according to the TMC.
Definition PsyTime.cpp:69
std::string PrintTimeDifString(uint64 t, bool us=true, bool ms=true)
Definition PsyTime.cpp:722
int32 GetTimeAgeMS(uint64 t)
Age of a timestamp relative to now, in milliseconds.
Definition PsyTime.cpp:35
int64 GetTimeAge(uint64 t)
Age of a timestamp relative to now.
Definition PsyTime.cpp:25
THREAD_RET THREAD_FUNCTION_CALL RequestGatewayExecRun(THREAD_ARG arg)
Thread entry point for the gateway's executor-side loop.
THREAD_RET THREAD_FUNCTION_CALL RequestGatewayClientRun(THREAD_ARG arg)
Thread entry point for the gateway's client-side loop.
RequestStatus
Lifecycle states of a request as it moves through client, gateway and executor.
bool TextStartsWith(const char *str, const char *start, bool caseSensitive=true)
Test whether str starts with start.
Definition Utils.cpp:7240
bool Sleep(uint32 ms)
Suspend the calling thread.
Definition Utils.cpp:2802
uint32 Ascii2Uint32(const char *ascii, uint32 start=0, uint32 end=0)
Parse an unsigned 32-bit decimal integer from a substring.
Definition Utils.cpp:7526
std::vector< std::string > TextListSplit(const char *text, const char *split, bool keepEmpty=true, bool autoTrim=false)
Split text on a separator.
Definition Utils.cpp:6511
#define GETIPADDRESSQUADPORT(a)
Definition Utils.h:1447
const char * stristr(const char *str, const char *substr, uint32 len=0)
Case-insensitive strstr.
Definition Utils.cpp:6035
std::string StringFormat(const char *format,...)
printf into a std::string.
Definition Utils.cpp:6626
uint64 Ascii2Uint64(const char *ascii, uint32 start=0, uint32 end=0)
Parse an unsigned 64-bit decimal integer from a substring.
Definition Utils.cpp:7480
@ JSMN_UNDEFINED
Definition jsmn.h:29
std::string GetJSONChildValueString(jsmntok_t *tokens, int tokenCount, const char *json, const char *key, int parent, jsmntype_t type=JSMN_UNDEFINED)
Definition jsmn.cpp:441
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
uint64 GetJSONChildValueUint64(jsmntok_t *tokens, int tokenCount, const char *json, const char *key, int parent, jsmntype_t type=JSMN_UNDEFINED)
Definition jsmn.cpp:453
void jsmn_init(jsmn_parser *parser)
Create JSON parser over an array of tokens.
Definition jsmn.cpp:311
std::string AddToJSONRoot(const char *json, const char *subJSON)
Definition jsmn.cpp:539
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 char HTTP_Status[][128]
Full HTTP status lines (with inline error bodies for error cases), indexed by the HTTP_* status ids a...
static bool Test_RequestGateway()
void Register_RequestGateway_Tests()
Notification of a connection lifecycle change (connect, disconnect, buffer state.....
uint8 type
NETWORKEVENT_* event type.
Gateway-side record of one connected client or executor.
uint64 location
Remote endpoint packed as uint64.
uint32 longReqQProcessingSize
Peer-reported long requests currently processing.
uint64 lastConTime
Last successful connect/heartbeat time (ms epoch).
RequestQueue longReqQueue
Gateway-side queue of long requests for this peer.
RequestQueue shortReqQueue
Gateway-side queue of short requests for this peer.
void clear()
Reset all counters/ids to the disconnected state.
uint64 conID
NetworkManager connection id.
JSON parser.
Definition jsmn.h:65
JSON token description.
Definition jsmn.h:51